Gå til innhold

Anbefalte innlegg

La oss si at vi har en array s = ["22. mai sa han noe"]

hvordan får jeg splittet opp denne, slik at det blir noe slikt som dette: ["22. mai", "sa", "han", "noe"]? 

Som du kan se er det mellomrom mellom 22. og mai. Noe som fører til at .split() gir 22. og mai som 2 elementer. 

Lenke til kommentar
Videoannonse
Annonse

Det finnes ingen enkel måte å gjøre dette på. Spesifikasjonen din er i tillegg veldig tynn. Hvordan vil du f.eks behandle:

s = "I dag (10. juni) kom det noen"
Skal det blir a eller b?

a = ["I", "dag", "(", "10. juni", ")", "kom", "det", "noen"]
b = ["I", "dag", "(10. juni)", "kom", "det", "noen"]
Vil det i det hele tatt være mulig å splitte denne riktig:

s = "Tallet er 10. Juni er en fin måned."  # Legg merke til at dette er to selvstendige setninger
Vil datoen alltid være på formatet "dd. måned"? Hva hvis noen slenger på et årstall? Eller glemmer punktum?

 

For moro skyld prøvde jeg å snekre sammen noe. Den fungerer på eksempelet ditt, men den feiler allerede på mitt eksempel. Den kommer til å feile på en haug med andre eksempler også. Og den er allerede så stor og stygg og hacky at det vil være umulig å verken vedlikeholde eller utvide den.

import datetime
import locale
locale.setlocale(locale.LC_ALL, '')


def split_except_date(s):
    parts = s.strip().split()

    # Spesifiser datoformater
    formats = [
        '%d. %b',  # 22. sep
        '%d. %B',  # 22. september
    ]

    # Lag alle kombinasjoner av to etterfølgende ord
    combinations = zip(parts[:-1], parts[1:])

    dates = {}  # {elementnummer: dato}
    # Sjekk om noen av kombinasjonene inneholder en dato
    for i, combination in enumerate(combinations):
        s_test = ' '.join(combination)
        for format in formats:
            try:
                date = datetime.datetime.strptime(s_test, format)
                dates[i] = date.strftime(format)
                break
            except ValueError:
                pass

    # Sett sammen en ny liste basert på `parts` og elementene som
    # inneholder datoer.
    new_parts = []
    for i in range(len(parts)):
        if i in dates:
            new_parts.append(dates[i])
        elif (i-1) in dates:
            # Hopp over dette elementet siden det er inkluder
            # i forrige element.
            continue
        else:
            new_parts.append(parts[i])
    return new_parts

s = ("22. mai sa han noe, men 11. januar ble det slutt. "
     "I dag (10. jun) er det derimot sol.")
print(split_except_date(s))
Jeg har en følelse av at du prøver å løse et problem som er langt større enn du tror. Eller at du egentlig burde løse det på en helt annen måte. Hva er det du egentlig prøver å løse?
  • Liker 1
Lenke til kommentar

Det jeg prøver å løse er å få med ord som har punktum med mellomrom slikt som "22. mai", "test. 15". Hvor jeg vil ha med punktum. Når man bruker .split() så splittes eksempel 22. mai til 22. og mai, altså på to ord noe jeg vil at den ikke skal gjøre. Hvis jeg får til dette er jeg fornøyd. 

Lenke til kommentar

Det jeg prøver å løse er å få med ord som har punktum med mellomrom slikt som "22. mai", "test. 15". Hvor jeg vil ha med punktum. Når man bruker .split() så splittes eksempel 22. mai til 22. og mai, altså på to ord noe jeg vil at den ikke skal gjøre. Hvis jeg får til dette er jeg fornøyd.

Hva med RegExp? Denne koden burde funke. Den kan nok forbedres en del, men her har du iallefall en løsning du kan jobbe ut i fra.

 

import re
s = "22. mai sa han noe test. 15 jeg er halla. hallo jeg er her"
print(s)
ptrn = r'([\d]+\. [a-zA-Z]+|[a-zA-Z]+\. [0-9]+|[a-zA-Z\.]+)'
parts = list(filter(None,re.split(ptrn,s)))
parts = list(filter(lambda x: x != ' ',parts))
print(parts)
EDIT: Gitt at jeg forsto spørsmålet ditt rett.

 

EDIT2: Du må kanskje utvide koden litt for å ta høyde for flere formater i teksten din. Jeg er ikke sikker på om den lille løsningen min vil kunne fange opp alt du vil at den skal.

Endret av Flin
Lenke til kommentar

my_str = "a b c 10. mai d"
result = [el.replace(".☻", " ") for el in my_str.replace(". ", ".☻").split()]
Edit: Du kan selvsagt bruke et annet symbol, bare bruk et som ikke vil finnes i teksten.

 

Edit 2: Dette er for øvrig et bruksområde for tegnet "hardt mellomrom". Bruk "hardt mellomrom" der du vil at det ikke skal brytes opp, f.eks. etter replikkstrek og i datoer.

Endret av Emancipate
Lenke til kommentar

Det jeg prøver å løse er å få med ord som har punktum med mellomrom slikt som "22. mai", "test. 15". Hvor jeg vil ha med punktum. Når man bruker .split() så splittes eksempel 22. mai til 22. og mai, altså på to ord noe jeg vil at den ikke skal gjøre. Hvis jeg får til dette er jeg fornøyd.

Da blir det enklere.

def split_except_point(s):
    parts = s.strip().split()
    new_parts = []
    it = iter(range(len(parts)))
    for i in it:
        if parts[i].endswith('.') and i < len(parts) - 1:
            new_parts.append(' '.join(parts[i:i+2]))
            next(it)  # Hopper over neste iterasjon
        else:
            new_parts.append(parts[i])
    return new_parts

s = ("22. mai sa han noe, men 11. januar ble det slutt. "
     "I dag (10. jun) er det derimot sol.")
print(split_except_point(s))
Fremdeles ikke fryktelig pent pga iterator og next(), men i hvert fall enklere enn regexp og "magic symbol". Klarer ikke å bestemme meg om den er bedre enn denne:

def split_except_point(s):
    parts = s.strip().split()
    new_parts = []
    for i in range(len(parts)):
        if parts[i].endswith('.') and i < len(parts) - 1:
            new_parts.append(' '.join(parts[i:i+2]))
        elif i > 0 and parts[i - 1].endswith('.'):
            continue
        else:
            new_parts.append(parts[i])
    return new_parts

s = ("22. mai sa han noe, men 11. januar ble det slutt. "
     "I dag (10. jun) er det derimot sol.")
print(split_except_point(s))
Lenke til kommentar
from functools import reduce

def join_dot(a, b):
middle = [a[-1] + '. ' + b[0]]
left = a[:-1]
right = b[1:]
return left + middle + right

def split_dot(s):
presplit = [el.split() for el in s.split('. ')]
return reduce(join_dot, presplit)

s = "a b c. d e f. g h"
#s = "Her. er. 1 eksempel til deg."

print(split_dot(s))
Endret av Emancipate
Lenke til kommentar

 

Fremdeles ikke fryktelig pent pga iterator og next(), men i hvert fall enklere enn regexp og "magic symbol". Klarer ikke å bestemme meg om den er bedre enn denne:

Bro, why you haitin' on regexp?

 

Jeg er for lat og dum til å stole på mine egne evner når det kommer til regexp.

https://www.xkcd.com/1171/

 

Kjernen i problemet er at regexp i prinsippet introduserer et ekstra språk i språket ditt. Og det er veldig vanskelig å faktisk verifisere en regexp.

Kun ved å kikke kjapt på regexp'en din lenger oppe, mener jeg at den vil tryne på non-ASCII tegn. UTF-8 er allerede her, og det finnes egentlig ingen gode grunner til å tenke ASCII lenger.

Lenke til kommentar

 

 

Fremdeles ikke fryktelig pent pga iterator og next(), men i hvert fall enklere enn regexp og "magic symbol". Klarer ikke å bestemme meg om den er bedre enn denne:

Bro, why you haitin' on regexp?

 

Jeg er for lat og dum til å stole på mine egne evner når det kommer til regexp.

https://www.xkcd.com/1171/

 

Kjernen i problemet er at regexp i prinsippet introduserer et ekstra språk i språket ditt. Og det er veldig vanskelig å faktisk verifisere en regexp.

Kun ved å kikke kjapt på regexp'en din lenger oppe, mener jeg at den vil tryne på non-ASCII tegn. UTF-8 er allerede her, og det finnes egentlig ingen gode grunner til å tenke ASCII lenger.

 

Greit nok.

 

Du har rett i at det er mye den lille koden der ikke vil håndtere. Hvis et av kravene er at man skal kunne håndtere alle symbolene i UTF-8 standarden så må man legge til noen ledd. Jeg er relativt sikker på at eksemplet mitt vil få problemer for langt enklere tilfeller også, tilfeller med kun ASCII tegn.

 

Trådstarter må vite hva rammene til problemet er og så forsikre seg om at en eventuell løsning kan håndtere alle mulighetene innen for de begrensningene. RegExp kan gjøre det, men man må nok tilpasse det eksemplet jeg kom med.

Lenke til kommentar

Trådstarter må vite hva rammene til problemet er og så forsikre seg om at en eventuell løsning kan håndtere alle mulighetene innen for de begrensningene. RegExp kan gjøre det, men man må nok tilpasse det eksemplet jeg kom med.

I slike tilfeller med mange kompliserte krav og mye tilpassing ender du fort opp med en regexp som vokser seg stor og uhåndterlig. Da kan du like gjerne droppe hele regexp'en og bruke et ordentlig parsing-bibliotek i stedet. Regexp blir i alle tilfeller liggende i et litt ullent området mellom hel-manuell parsing (split, for, if, etc.) og ordenlig parsing med et parsing-bibliotek. Både manuell parsing og parsing-biblioteker er vanligvis eksplisitte og fine, mens regexp fort inkluderer mange implisitte detaljer som brekker hele PEP 20.

Det er ingen tvil om at regexp _kan_ gjøre det du ønsker, men det viktige spørsmålet er om du _bør_ gjøre det med regexp. Du kan fint skrive Fortran-kode i hvilket som helst språk, men det er ikke nødvendigvis en god idé. Regexp er i denne sammenhengen et verktøy som er tett knyttet til Perl og POSIX, og som ikke nødvendigvis hører så godt hjemme i andre språk.

Lenke til kommentar

Personlig så føler jeg også at regex er mye knot - overcomplicated and underpowered. Hvorfor skal jeg lære meg et nytt språk med mindre funksjonalitet enn det jeg kan, som i tillegg er uleselig?

 

Problemet med regex, er at det ikke er støtte for rekursive regler og (re)faktorisering (altså å flytte kode ut av regexen, så den ikke blir for lang og kompleks).

Endret av Emancipate
Lenke til kommentar

Mulig det er fordi jeg egentlig ikke er en programmerer, bare en fyr som programmerer mye, men jeg liker RegExp. Det, har som alt andre ting, sin begrensninger, men særlig hvis man bruker en god "regexp-maker" så tar det ikke så lang tid å snekre sammen noe som fungerer.

 

Vi er langt off topic nå, men jeg synes det er et interessant å høre hva folk foretrekker og hvorfor. Det virker som om det ofte går et skille som avhenger av hvordan/hvorfor noen har lært å programmere.

Lenke til kommentar
  • 5 måneder senere...

Dette er en gammel tråd, men jeg holder på å lære meg litt Python og syntes oppgaven var interessant. Her er hva jeg gjorde:

 

Utfører først en vanlig split:

s = '22. mai sa han noe.'
the_split = s.split()
print(the_split)
>> ['22.', 'mai', 'sa', 'han', 'noe.']
Lager meg en liste med unntak til regelen "punktum avslutter setning":

months = ['jan', 'feb', 'apr', 'mai', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'des']
Går igjennom split-listen og tester unntakene:

for i in range(0, len(the_split):
    if the_split[i][-1] == '.':
        if i < len(the_split)-1:
            if the_split[i+1] in months:
                the_split[i] = the_split[i] + ' ' + the_split[i+1]

print(the_split)
>> ['22. mai', 'mai', 'sa', 'han', 'noe.']
Ender opp med et element for mye, må holde styr på det og fjerne det fra listen, prøver på nytt:

 

 

remove_list = list()
for i in range(0, len(the_split):
    if the_split[i][-1] == '.':
        if i < len(the_split)-1:
            if the_split[i+1] in months:
                the_split[i] = the_split[i] + ' ' + the_split[i+1]
                remove_list.append(i+1)

for index in remove_list:
    the_split.__delitem__(int(index))

print(the_split)
>> ['22. mai', 'sa', 'han', 'noe.']
Smekker det hele inn i en funksjon:

 

def split_not_months(string):

    the_split = string.split()
    remove_list = list()

    for i in range(0, len(the_split)):
        if the_split[i][-1] == '.':
            if i < len(the_split)-1:
                if the_split[i+1] in months:
                    the_split[i] = the_split[i] + ' ' + the_split[i+1]
                    remove_list.append(i+1)
    for index in remove_list:
        the_split.__delitem__(int(index))

    return the_split
Problem: Si du har utvidet listen av måneder til å inkludere varierte måter å skrive den samme måneden, eksempel Jan for januar (kanskje med en dictionary), hva nå om en setning starter med "Jan" som et navn på en person?

 

En løsning: Lag en hjelpefunksjon som sjekker om to elementer i den opprinnelige splittede listen til sammen utgjør et lovlig uttrykk for en dato:

 

def valid_date(s1, s2):
    if s2 in months:
        if s1[-1] == '.':
            numpart = s1[:-1]
            if numpart.isdigit():
                if 1 <= int(numpart) <= 31:
                    return True
    else:
        return False


def split_not_months(string):
    the_split = string.split()
    remove_list = list()

    for i in range(0, len(the_split)):
        if valid_date(the_split[i-1], the_split[i]):
            the_split[i-1] = the_split[i-1] + ' ' + the_split[i]
            remove_list.append(i)

    for index in remove_list:
        the_split.__delitem__(int(index))

    return the_split

print(split_not_months('22. mai sa han noe.'))
>> ['22. mai', 'sa', 'han', 'noe.']
Og i tillegg:

 

print(split_not_months('22. mai sa han noe. Jan sa ingenting.'))
>> ['22. mai', 'sa', 'han', 'noe.', 'Jan', 'sa', 'ingenting.']
Her er ikke valid_date helt riktig. Februar har for eksempel ikke 31 dager, men jeg holdt den kort for enkel-hetens skyld. Dette kunne igjen vært løst med en dictionary. Endret av Denjam
Lenke til kommentar

Opprett en konto eller logg inn for å kommentere

Du må være et medlem for å kunne skrive en kommentar

Opprett konto

Det er enkelt å melde seg inn for å starte en ny konto!

Start en konto

Logg inn

Har du allerede en konto? Logg inn her.

Logg inn nå
  • Hvem er aktive   0 medlemmer

    • Ingen innloggede medlemmer aktive
×
×
  • Opprett ny...