Lektion 7: Arbeta med text
Begrepp som diskuteras: | Strängar, lexikon, sorteringar, reguljära uttryck. |
Arbetssätt: | Arbeta gärna tillsammans med någon, men skriv egen kod. Diskutera med varandra!
Försök göra övningarna innan du tittar på svaren! Fråga handledarna om det är något svar du inte förstår! |
Uppskattad arbetstid: | 6 timmar. |
Redovisning: | Obligatorisk redovisning av uppgifter enligt kurssidan. |
Textsträngar
Hittills har vi huvudsakligen använt textsträngar (typen str
) som variabelvärden och i utskrifter.
I denna lektion ska vi gå in på mer detaljer strängar.
Mycket av det vi gjort med listor kan vi göra med strängar. Strängar är dock, precis som tupler, oföränderliga dvs om vi vill ändra på något i en sträng så får vi göra en ny sträng.
Läs igenom minilektionen om strängar för att se några vanliga operationer på strängar.
Vi definierar en sträng att använda i vår exempelkod:
Denna sträng innehåller bokstäver, skiljetecken, blanktecken och radslutstecken. Radslutstecknen syns normalt inte men gör så att texten blir raduppdelad vid utskrift:
Räkna tecken och strängar
Kod | Värde | Kommentar |
---|---|---|
len(text) | 111 | Totala antalet tecken. |
text.count(' ') | 12 | Antalet blanktecken. |
text.count('n ') | 4 | Observera att 'n' i slutet av raderna inte följs av ett blanktecken. |
text.count('\n') | 4 | Antalet rader (egentligen antalet radslutstecken — sista raden behöver inte nödvändigtvis följas av ett radslutstecken). |
Räkna bokstäver
Antag att vi vill räkna antalet bokstäver i texten.
Ett uppenbart sätt att göra det på är att gå igenom strängen tecken för tecken och räkna de tecken är bokstäver.
Anropet s.isalpha()
returnerar True
returnerar om
strängen s
bara innehåller bokstäver.
print(len([c for c in text if c.isalpha()]))
Räkna "svenska" bokstäver
Metoden isalpha
fungerade uppenbarligen även för svenska bokstäver och den fungerar även
för ett stort antal andra nationella tecken som t ex ü, é, á, è, î, Ç, ô, och Á.
Det finns ingen metod "isswedish
" motsvarande isalpha
utan vi får använda operatorn
in
för att se om ett tecken är å, ä eller ö:
lower
som
returnerar en sträng där all versaler är utbytta till motsvarande gemena.
Det hade naturligtvis gått lika bra att istället testa mot strängen 'åäöÅÄÖ'
.
(Anmärkning:* Försök modifiera koden i anmärkningen i föregående uppgift så att den fungerar i denna uppgift!)
Övningar
-
Skriv kod som räknar och skriver ut antalet nationella bokstäver dvs bland
annat å, ä, ö, ü, é, á, è, î, Ç, ô, Á, ...
Ledning: Det är lättare att känna till de internationella bokstäverna än alla nationella!test_text = 'aVcdåäöÅÄÖüéáèîçÇôÜÁxyZX' iletters = 0 letters = 0 for c in test_text: if c.isalpha(): letters += 1 if c.lower() in 'abcdefghijklmnopqrstuvw': iletters += 1 print(f'Antal nationella bokstäver: {letters - iletters}')
Byta ut tecken
Antag att vi vill ta "internationalisera" texten genom att byta ut å mot aa, ä mot ae och ö mot oe.Metod 1: Metoden replace
replace
returnerar.
Metod 2: Använd ett lexikon
Vi kan skapa en översättningstabell med hjälp av ett lexikon ("dictionary" - se minilektionen om lexikon!) Vi lägger in de svenska bokstäverna som nycklar och motsvarande internationella teckenkombinationer som värden:Räkna bokstavsfrekvenser
Antag att vi vill räkna bokstavsfrekvenser. Ett lexikon med enskilda bokstäver som nycklar och antal förekomster som värde fungerar bra:
I minilektionen om lexikon beskrivs flera sätt att iterera över
ett lexikon.
I detta fall väljer vi att först skapa en lista av tupler med nyckel-värdepar som vi sorterar
med hjälp av listobjektets sort
-metod:
Sortering på frekvens
När man, som vi gjort ovan, sorterar en lista med tupler så sker sorteringen på det första värdet i tupeln (dvs på bokstaven). För att sortera på frekvens måste man, på något sätt, ange att det är frekvensen som ska användas som sorteringsnyckel, inte bokstaven.
Både till standardfunktionen sorted
och till metoden sort
för list-objekt
kan man skicka med en funktion som anger hur komponenterna ska jämföras.
Om listelementen består av tupler (som i vårt fall) skriver vi en funktion:
-
Vi ger funktions-namnet som värde till
key
-parametern. Sorteringsfunktionen kommer att anropa denna funktion för varje element i listan för att få fram ett "sorteringsvärde" för elementet. -
Parametern
reverse=True
anger att att resultatet ska bli i fallande ordning dvs med största värdet först. -
Vi har använt tabulatorteckenet som värde på
end
-parametern tillprint
Övningar
-
Sätt
l = ['alpha', 'bravo', 'charlie', 'delta', 'echo', 'foxtrot']
Vad blir resultatet avl.sort(key=part2)
?En lista sorterad på den andra bokstaven i orden:['echo', 'delta', 'charlie', 'alpha', 'foxtrot', 'bravo']
-
Ett annat sätt att åstadkomma sorteringen på frekvenser är att utifrån
variabeln
lista
göra en ny lista där man byter ordning på frekvens och bokstav och sedan sorterar. Skriv koden för detta!För den som läst på "list comprehensions" går det elegant på detta sättswapped = sorted([(x[1], x[0]) for x in lista], reverse=True)
Det går naturligtvis också att bygga upp listan med en for-loop:swapped = [] for x in lista: swapped.append((x[1], x[0])) swapped.sort(reverse=True)
Ordanalyser
Hittills har det mesta handlat om att göra saker med enskilda bokstäver men nu ska vi göra liknande analyser av orden i en text.
Att hitta ord i en text - reguljära uttryck
Det första problemet är att lokalisera ord i en sträng. I minilektionen om strängar nämns en metodsplit
som vid första anblicken verkar användbar.
Minlektionens exempel är koden
['Ta', 'det', 'lugnt']
.
Om vi använder den metoden på Stagnelius text i början av lektionen dvs
om vi gör print(text.split(' '))
får vi följande nedslående utskrift:
Vi behöver något sätt att tala om att de "ord" vi är ute efter enbart får innehålla bokstäver. I andra sammanhang, t.ex. vi analys av texten i ett Python-program, vill vi kanske tillåta understrykningstecknen att ingå i orden.
Det finns ett mycket generellt verktyg som kan hjälpa oss med detta: reguljära uttryck. Med sådana kan man definiera mönster och leta efter dessa i en text. Matchande delar av texten kan också bytas ut på intrikata sätt.
Reguljära uttryck är inte alls begränsade till Python utan kan hanteras av många programmeringsspråk och editorer.
I denna kurs tar vi bara upp några mycket enkla exempel men råder den som ska arbeta med text att sätta sig den världen. Det finns mycket material på nätet.
För att plocka ut ord i betydelsen "en följd av en eller flera bokstäver" gör vi på följande sätt:
-
Reguljära uttryck definieras i paketet
re
som alltså importeras. -
Parametern till
re.findall
är en "rå sträng" dvs den börjar medr'
(ellerr"
) i stället för med bara'
(eller"
). I en rå sträng tolkas inte escape-tecknet på vanligt sätt (tab, newline, ...) utan behålls och har andra betydelser i reguljära uttryck. -
Uttrycket
[a-zA-ZåäöÅÄÖ]
matchar vilket tecken som helst mellan a och z, mellan A och Z samt å, ä, ö och Å, Ä, Ö dvs en internationell eller 'svensk' bokstav. -
Tecknet
+
efter[]
-gruppen anger att det kan vara en eller flera bokstäver. - Mönstret matchar helt enkelt en obruten följd av en eller flera bokstäver.
-
Mönstermetoden
findall
returnerar alltså en lista av alla matchande sekvenser.
Obligatoriska uppgifter
-
Skriv ett program som läser en textfil och producerar statistik.
Programmet ska skriva ut det totala antalet ord och antalet olika ord.
Programmet ska också ge en lista på de n vanligaste orden och
de m ovanligaste orden. Värdena på m och n ska läsas
in med
input
. Se minilektionen Läsa och skriva som beskriver hur man läser en fil. -
Gör en variant av programmet som läser en fil (typiskt ett datorprogram).
Programmet ska producera en lista med radnummer över filen och en referenslista som för varje
ord anger på vilka rader det förekommit.
Python-ord som
for
,if
,def
... ska inte tas med i listan liksom inga ord från kommentarerna.Exempel: Om koden som räknar bokstavsfrekvenser ovan används som indata ska utskriften bli:
1 2 freq = {} # Skapa ett tomt lexikon 3 for c in text: # Iterera över tecknen 4 if c.isalpha(): # Vi intresserar oss bara för bokstäver 5 c = c.lower() # Skiljer inte på små och stora 6 if c in freq: # Om denna bokstav redan finns som nyckel 7 freq[c] += 1 # Öka dess frekvens 8 else: # annars 9 freq[c] = 1 # Lägg in den med frekvensen 1 10 print(freq) Referenslista: c [3, 4, 5, 5, 6, 7, 9] freq [2, 6, 7, 9, 10] isalpha [4] lower [5] print [10] text [3]
line
är en rad med Python-kod så blir eventuell kommentar
borttagen med raden
line = re.sub(r'#.*$', '', line)
r'#.*$'
tolkas så här:
-
#
matchar naturligtvis kommentartecknet - Punkten
.
matchar vilket tecken som helst (utom radbrytning) -
*
är ett "metatecken" som anger att det närmast förgående matchningen upprepas 0 eller flera gånger $
är också ett metatecken som anger strängslut.
re.sub(r'#.*$', '', line)
returnerar alltså en sträng där allt från
och med tecknet #
till slutet är utbytt mot en tom sträng.
Fråga
Hur många timmar har du arbetat med denna lektion?
Gå till nästa lektion eller gå tillbaka