1.4 Strenger, f-strenger og formatert utskrift
Strengoperasjoner, f-strenger med formatspesifikatorer, og `print` med `end`/`sep` — utskriftsdetaljene sensor krever nøyaktig.
- Sjanger A — kodesporing («hva skriver dette programmet ut?»), som forekommer i 100 % av settene. Her er kravet at utskriften angis nøyaktig: riktig antall mellomrom, riktige hakeparenteser, riktig antall desimaler. Et riktig tall med feil formatering er et tapt poeng.
- To feller er gjennomgående: end=' ' som fjerner linjeskiftet, og f-strengens bredde- og presisjonsangivelse. Sammen utgjør de feilkode #16.
- Strengmetodene .split() og .strip() er i tillegg ren forkunnskap til fillesing (sjanger L — fillesing til nøstet ordbok, altså å lese en datafil inn i en oppslagsstruktur; den forekommer i 100 % av settene og gir 8–12 poeng). Se kap. 8.1.
Prioritet: dette må sitte (høyeste av bokas tre nivåer). Ikke fordi stoffet er dypt, men fordi det er her billige poeng forsvinner: du kan ha spore riktig hele veien og likevel skrive utskriften feil.
- Kap. 1.1 — Variabler, datatyper, uttrykk og operatorpresedens: at '3' og 3 er to ulike verdier, og at print skiller argumentene sine med ett mellomrom.
- Kap. 1.2 — Lister, indeksering og slicing: indeksering og slicing — de samme reglene gjelder for strenger.
- Kap. 1.3 — Løkker: for-løkker, som vi bruker til å skrive ut tabeller.
Løkke 1 — Strengen er en sekvens (~10 min)
Målestasjonen leverer én linje per døgn, og den ser slik ut:
Blindern 21.5 4.0Alt du har er tekst. Skal du regne på tallene, må du først dele opp linja, rydde vekk usynlige tegn, og konvertere til tall. Det er hele fillesingsoppgaven i miniatyr, og den begynner med at en streng er en sekvens av tegn — helt som en liste.
En streng er en ordnet rekke tegn, og alt du kan gjøre med en liste av indeksering og slicing, kan du gjøre med en streng.
tekst[0] er første tegn, tekst[-1] er siste, tekst[1:4] er et utsnitt på tre tegn, og tekst[::-1] er teksten baklengs. len(tekst) er antall tegn, mellomrom medregnet.
Én forskjell fra lister: indeksering i en streng gir en streng (av lengde 1), ikke en annen datatype. Det finnes ingen egen «tegn»-type i Python.
En streng kan ikke endres etter at den er laget. tekst[0] = 'A' stopper programmet med en TypeError.
Alle strengmetodene du møter nedenfor — .strip(), .replace(), .upper() — returnerer en ny streng og lar den gamle være. Derfor må du ta imot resultatet: tekst = tekst.strip(). Å skrive bare tekst.strip() som en egen linje gjør ingenting, og det er en stille feil som koster poeng i fillesingsoppgaver.
+ skjøter sammen: 'ab' + 'cd' gir 'abcd'. * gjentar: '-' * 5 gir '-----'.Begge operandene til + må være strenger. 'sum: ' + 4 stopper programmet med en TypeError — du må skrive 'sum: ' + str(4), eller helst bruke en f-streng. Gjentakelsen med * er derimot praktisk til å lage skillelinjer i en tabell.
På strenger er in en delstreng-test, ikke en elementtest: 'ind' in 'Blindern' er True.
Det er en viktig forskjell fra lister, der in bare finner hele elementer. Testen skiller mellom store og små bokstaver, så 'blindern' in 'Blindern' er False.
Antall tegn i strengen, medregnet mellomrom og eventuelle usynlige tegn som linjeskift.
Det er nettopp derfor len er et diagnoseverktøy ved fillesing: er lengden én mer enn du forventet, henger det sannsynligvis et linjeskift på slutten, og da trenger du .strip().
Hva skriver programmet ut?
navn = 'Blindern'
print(navn[0], navn[-1])
print(navn[1:4])
print(len(navn))
print(navn[::-1])
print('ind' in navn, 'X' in navn)
print('-' * 8)Utskrift:
B n
lin
8
nrednilB
True False
--------Alt her er reglene fra kap. 1.2, brukt på tegn i stedet for på tall: navn[1:4] gir tre tegn fordi slutten er eksklusiv, og navn[::-1] snur rekkefølgen.
Merk at utskriften ikke har fnutter rundt seg. Fnuttene finnes bare i koden.
(Innstegsoppgave — minimal sporing. Sjanger A — kodesporing, altså «hva skriver programmet ut?».) La tekst = 'Tryvann'.
a) Hva gir tekst[0]?
b) Hva gir tekst[-2]?
c) Hva gir len(tekst)?
d) Hva gir 'van' in tekst?
Prøver du å bytte ut ett tegn direkte, stopper programmet:
navn = 'Blindern'
navn[0] = 'K'
print(navn)Utskrift: ingen (programmet stopper med en feilmelding før noe blir skrevet). Siste linje i feilmeldingen er:
TypeError: 'str' object does not support item assignmentFeiltypen er TypeError, en av kandidatene i exception-tabellene i kap. 2.2. Vil du ha en endret streng, må du lage en ny: navn = 'K' + navn[1:].
Den praktiske konsekvensen er regelen fra definisjonen over: alle strengmetodene returnerer en ny streng, og du må ta imot resultatet. linje.strip() alene gjør ingenting.
Løkke 2 — Å dele opp og rydde en tekstlinje (~10 min)
Nå til de fire metodene du får bruk for i hver eneste fillesingsoppgave. Rekkefølgen er alltid den samme: rydd, del opp, konverter.
Deler strengen i en liste av delstrenger. Med et argument deles den på nøyaktig det tegnet: 'a;b;c'.split(';') gir ['a', 'b', 'c'].
Uten argument deles den på vilkårlig blanktegn — ett eller flere mellomrom, tabulator eller linjeskift — og eventuelle blanktegn i begge ender kastes. Det gjør linje.split() robust mot ujevn kolonnebredde.
Fellen: bruker du bar split() på en fil der feltene er skilt med semikolon, får du én lang delstreng i stedet for flere. Det er feilkode #8, og den slår først ut når du prøver å konvertere til tall. Bruk split(';') når separatoren er et bestemt tegn.
Fjerner blanktegn i begge ender av strengen og returnerer en ny streng. Blanktegn inne i strengen røres ikke.
Den viktigste jobben .strip() gjør, er å fjerne linjeskiftet som følger med hver linje du leser fra fil. Uten den blir det siste feltet '4.0\n' i stedet for '4.0', og float klarer faktisk det — men int('4\n') og enhver sammenligning med en tekstverdi gjør det ikke. Å glemme .strip() er feilkode #7.
Motstykket til split: skjøter sammen elementene i en liste av strenger til én streng, med sep mellom. ';'.join(['a', 'b']) gir 'a;b'.
Merk hvor separatoren står: den er strengen du kaller metoden på, ikke et argument. Og alle elementene må være strenger — ', '.join([1, 2]) stopper programmet. Er de tall, må du gjøre dem om først.
Gir posisjonen der delstrengen begynner: 'Blindern'.index('n') gir 2, altså den første forekomsten.
Finnes ikke delstrengen, stopper programmet med en ValueError. Vil du bare vite om den finnes, bruk in — den gir True/False og feiler aldri.
.lower() og .upper() gir en ny streng med bare små eller bare store bokstaver. De brukes til å gjøre en sammenligning uavhengig av store bokstaver: svar.lower() == 'ja'..replace(gammel, ny) bytter ut alle forekomster og gir en ny streng: '12,5'.replace(',', '.') gir '12.5'. Nettopp den konverteringen trenger du hvis en datafil bruker komma som desimalskilletegn, for float('12,5') stopper programmet.
Én linje fra målestasjonen ser slik ut, med et linjeskift på slutten:
Blindern ; 21.5 ; 4.0Skriv koden som henter ut stasjonsnavnet og de to tallene.
Rekkefølgen er rydd, del opp, konverter — og hvert felt må ryddes for seg, fordi det står mellomrom rundt semikolonene:
linje = ' Blindern ; 21.5 ; 4.0\n'
deler = linje.split(';')
print(deler)
navn = deler[0].strip()
temp = float(deler[1])
vind = float(deler[2])
print(navn, temp, vind)
print(len(deler), len(navn))Utskrift:
[' Blindern ', ' 21.5 ', ' 4.0\n']
Blindern 21.5 4.0
3 8Tre ting å legge merke til:
1. Utskriften av deler viser hvor mye søl som ligger i feltene: mellomrom foran og bak, og linjeskiftet i det siste feltet. Fnuttene og \n synes fordi Python viser lister med kodeformen til hvert element.
2. float tåler blanktegn rundt tallet, så float(' 21.5 ') går fint. Men navn må ryddes med .strip(), ellers får du to mellomrom foran.
3. Hadde vi brukt bar linje.split() her, ville vi fått fem deler — Blindern, ;, 21.5, ;, 4.0 — og feil felt på hver plass. Det er feilkode #8.
(Midtveisnivå, sjanger A.) Hva skriver programmet ut?
linje = 'sol;regn;sno'
deler = linje.split(';')
print(deler)
print(len(deler))
print(deler[1].upper())
print('-'.join(deler))Løkke 3 — F-strenger: å sette verdier inn i tekst (~12 min)
— naturlig pausepunkt —
Nå skal utskriften bli pen. En f-streng er en streng med f foran fnuttene, der alt du setter i krøllparentes blir regnet ut og satt inn. Det er den moderne, korte måten, og det er den sensor forventer.
En streng med f rett foran fnutten. Inne i krøllparentesene kan du skrive hvilket som helst uttrykk, og verdien settes inn i teksten.
navn = 'Blindern'
temp = 21.5
print(f'{navn}: {temp} grader')
print(f'{navn} har {len(navn)} tegn, og {temp} + 1 er {temp + 1}')Utskrift:
Blindern: 21.5 grader
Blindern har 8 tegn, og 21.5 + 1 er 22.5Merk at f her er et prefiks på en streng, ikke en variabel. Det har ingenting å gjøre med funksjonen f som du skriver i ODE-oppgavene i kap. 7.1 — se advarselen om navnekollisjonen nedenfor.
Etter kolon kommer formatangivelsen. f'{x:8.3f}' betyr: bredde 8, 3 desimaler, f for fast desimalform.
- Bredden er totalt antall tegn feltet skal fylle, medregnet desimalpunktet og eventuelt minustegn. Er tallet kortere, fylles det ut med mellomrom foran (høyrejustert). Er det lengre, får det stå — bredden er et minimum, ikke et maksimum.
- Desimalene er alltid nøyaktig så mange som du ber om, med avrunding: f'{2.5:.0f}' gir 2, og f'{1.0:.3f}' gir 1.000.
Bredden er den som overses. f'{2.5:8.3f}' gir 2.500 med tre mellomrom foran, og de mellomrommene er en del av utskriften.
d er for heltall: f'{7:3d}' gir 7 med to mellomrom foran, altså bredde 3. Bruker du d på et flyttall, stopper programmet.e er eksponentform: f'{1234.5:.3e}' gir 1.234e+03 — ett siffer foran punktet, tre etter, og eksponenten med fortegn og minst to siffer. Den brukes til å vise svært små eller svært store tall, for eksempel en feil på .
En tabell over potenser av 2, med fast kolonnebredde. Hva skriver programmet ut?
for k in range(4):
print(f'{k:2d}:{2**k:5.2f}', end=' | ')
print()Utskrift:
0: 1.00 | 1: 2.00 | 2: 4.00 | 3: 8.00 | Sporingstavle
| Runde | k | f'{k:2d}' | 2**k | f'{2**k:5.2f}' | Legges til |
|---|---|---|---|---|---|
| 1 | 0 | 0 (ett mellomrom foran) | 1 | 1.00 (ett mellomrom foran) | 0: 1.00 \| |
| 2 | 1 | 1 | 2 | 2.00 | 1: 2.00 \| |
| 3 | 2 | 2 | 4 | 4.00 | 2: 4.00 \| |
| 4 | 3 | 3 | 8 | 8.00 | 3: 8.00 \| |
Regnestykket for bredden:
1.00 er fire tegn, og bredde 5 gir derfor ett mellomrom foran. 0 er ett tegn, og bredde 2 gir ett mellomrom foran.Fordi
end=' | ' erstatter linjeskiftet, havner alt på én linje, og linja avsluttes av det tomme print(). Legg merke til at det også står | etter den siste kolonnen — end gjelder hver runde, også den siste.(Midtveisnivå, sjanger A — formatert utskrift.) Hva skriver programmet ut? Skriv utskriften nøyaktig, med mellomrom.
x = 3.14159
print(f'{x:.2f}')
print(f'{x:8.3f}')
print(f'{x:.0f}')
print(f'{42:5d}|')Løkke 4 — print i detalj: end, sep og hvordan Python viser verdier (~10 min)
Til slutt de tre detaljene som avgjør flest kodesporingsoppgaver i hele faget.
end er det print skriver etter argumentene. Standardverdien er linjeskift, '\n'.Setter du end=' ', forsvinner linjeskiftet og neste utskrift fortsetter på samme linje. Setter du end='', kommer ingenting imellom i det hele tatt. Et tomt print() skriver bare linjeskiftet og brukes til å avslutte en linje du har bygget opp med end.
Dette er halve feilkode #16: en sporing der du skriver tre linjer i stedet for én, er feil selv om tallene er riktige.
sep er det som skrives mellom argumentene. Standardverdien er ett mellomrom.print(1, 2, 3, sep=';') gir 1;2;3, og print('a', 'b', sep='') gir ab. Merk at sep bare virker mellom argumenter — har print bare ett argument, har sep ingen effekt i det hele tatt. Det er en fin distraktor.
print viser en streng uten fnutter, men en liste med fnutter rundt hvert strengelement.Grunnen er at Python har to visningsformer: den lesbare (som print bruker på selve argumentet) og kodeformen (som brukes på elementer inne i en samling). Derfor:
print('sol')
print(['sol', 'regn'])
print(3.0, [3.0])Utskrift:
sol
['sol', 'regn']
3.0 [3.0]Regelen å ta med: lister skrives med hakeparenteser, komma og mellomrom, og strenger inne i dem beholder fnuttene.
Inne i en streng betyr \n linjeskift og \t tabulator. Det er to tegn i koden, men ett tegn i verdien — len('a\tb') er 3.
Tabulator hopper til neste faste kolonne og brukes til å justere tabeller:
print('navn\tgrader')
print('Blindern\t21.5')
print(len('a\tb'))Utskrift:
navn grader
Blindern 21.5
3Trenger du en backslash i selve teksten, skriver du \\. I bokas egen kode holder vi oss til fire mellomrom som innrykk og bruker aldri tabulator i Python-kildekoden (feilkode #24).
Fire linjer med print, tre ulike innstillinger. Hva skriver programmet ut?
print('a', 'b')
print('a', 'b', sep='')
print(1, 2, 3, sep=' ; ')
for i in range(3):
print(i, end='')
print()
print('slutt', end='!')Utskrift:
a b
ab
1 ; 2 ; 3
012
slutt!- print('a', 'b') gir mellomrommet Python setter inn av seg selv.
- sep='' fjerner det.
- sep=' ; ' setter inn hele den strengen mellom tallene — merk mellomrommene på begge sider.
- Løkka med end='' skriver tre sifre uten noe imellom og uten linjeskift; det tomme print() avslutter linja.
- Siste linje slutter med ! og ikke med linjeskift.
At det ikke kommer noe linjeskift til slutt, er verdt å merke seg i sporing: utskriften er slutt! uten mer.
Bokstaven f opptrer i to helt ulike roller i dette faget, og de har ingenting med hverandre å gjøre:
1. F-streng-prefikset: f'{x}'. Her er f en del av strengskrivemåten og står rett foran fnutten, uten mellomrom.
2. Funksjonen f: i matematiske oppgaver er f det vanlige navnet på en funksjon — def f(x): i kap. 1.6, og høyresiden def f(t, u): i ODE-oppgavene i kap. 7.1.
Praktisk konsekvens: har du definert en funksjon f, kan du fortsatt skrive f'{x}' — Python ser på om det står en fnutt rett etter. Men i en kodesporingsoppgave må du se det, og et alternativ som later som f'...' er et funksjonskall, er en reell distraktor.
Boka bruker derfor alltid f med backticks når det er kode, og skriver «f-streng» når det er skrivemåten.
(Midtveisnivå, sjanger A — end og sep.) Hva skriver programmet ut? Skriv utskriften nøyaktig, med mellomrom og linjeskift slik terminalen viser den.
for i in range(3):
print(i, i*i, sep=':', end=' ')
print()
print('a', 'b', 'c', sep='')(Midtveisnivå, sjanger A — strenger og lister i utskrift.) Hva skriver programmet ut?
ord = 'regn i mars'
deler = ord.split()
print(deler)
print(len(deler), len(ord))
print(deler[0][0], ord[0])
print(ord.replace(' ', '_'))Hva skriver programmet ut? Skriv utskriften nøyaktig; marker mellomrom nøye.
navn = ['Blindern', 'Tryvann']
temp = [21.5, 17.25]
for n, t in zip(navn, temp):
print(f'{n:10s}{t:6.1f}')
print(f'{"sum":10s}{sum(temp):6.1f}')Hva skriver programmet ut?
linje = ' Bygdoy ; 12,5 ; 3 \n'
deler = linje.split(';')
navn = deler[0].strip()
temp = float(deler[1].strip().replace(',', '.'))
antall = int(deler[2].strip())
print(len(deler))
print(f'[{navn}]')
print(temp, antall)
print(f'{navn.upper()}: {temp:5.2f} ({antall:2d})')- #16 — utskriftsdetaljer. end=' ' fjerner linjeskiftet; print setter ett mellomrom mellom argumentene; bredden i en f-streng fyller ut med mellomrom; lister skrives med hakeparenteser, komma og mellomrom. Dette er kapitlets hovedfelle.
- #8 — feil separator i split. Bar split() deler på blanktegn, ikke på semikolon. Er filen semikolonseparert, må det stå split(';').
- #7 — glemt .strip(). Siste felt på en fillinje får med linjeskiftet.
- Resultatet ikke tatt imot. Strenger er immutable, så linje.strip() alene gjør ingenting. Det må stå linje = linje.strip().
- 'sum: ' + 4. Du kan ikke skjøte en streng og et tall. Bruk en f-streng.
- f'{x:3d}' på et flyttall. d krever heltall; bruk f for desimaltall.
Begrepsbank
Begrepsbanken er flashcard-/repetisjonsstoff — den gjentar det du nettopp har lest. Hopp trygt over ved førstegangslesing; tidsanslaget for kapitlet gjelder kjernestoffet.
Strengen uten tegn. len('') er 0, og den regnes som falsk i en ja/nei-test, akkurat som den tomme lista.
Den oppstår oftere enn du tror: 'a;;b'.split(';') gir ['a', '', 'b'], altså med en tom streng i midten. Ved fillesing er en tom linje derfor noe du må håndtere før du prøver å konvertere til tall.
'12' og 12 er to helt ulike verdier. Tekst kan skjøtes og gjentas; tall kan legges sammen og ganges.Alt som kommer inn utenfra — fra kommandolinjen (kap. 2.1), fra input, fra en fil (kap. 8.1) — er tekst, og må konverteres med int eller float før du kan regne. Glemmer du det, får du '12' + '5' som blir '125' i stedet for 17. Det er en av de faste feilene i «finn feilen»-oppgaver.
'sol' og "sol" er samme verdi. Valget er praktisk: skal teksten inneholde en apostrof, bruker du doble fnutter, og omvendt.Boka bruker enkle fnutter som standard, fordi det er den vanlige stilen i fagets fasitkode. Inne i en f-streng må du dessuten passe på: bruker du enkle fnutter utenfor, må du bruke doble inni krøllparentesene, som i f'{"sum":10s}'.
Dette kapitlet er skrevet av Anthropics toppmodeller (Claude Opus og Claude Fable) og er foreløpig ikke manuelt gjennomgått — kvalitetskontrollen gjøres av uavhengige KI-agenter, og innmeldte feil rettes fortløpende. Funnet en feil? Meld fra, så retter vi den. Les mer om hvordan innholdet lages.
Skolesaga er en uavhengig læringsressurs og er ikke tilknyttet eller godkjent av Universitetet i Oslo. Dette er ikke offisielt studiemateriell. Les mer.