Tilbake
3.4

3.4 Formatert utskrift

Pen kolonneutskrift og tidsformat — rjust/ljust/center, nullpadding, format og f-strenger, med print-detaljene sensor krever nøyaktig.

40 min
6 oppgaver
Formatert utskrift
Din fremgang i kapitlet
0 / 6 oppgaver
Kapitlets plass i kurset
Forkunnskaper

- kap. 3.3 — strenger: skjøting med +, len og at en streng ikke kan endres. Justeringsmetodene i dette kapitlet er strengmetoder.
- kap. 1.1 — datatyper og konvertering med str(), int() og float(), og forskjellen på / og //.
- kap. 1.3 — løkker, fordi en tabell skrives ut én rad om gangen.

Vil du se den samme formateringen behandlet i dybden i et parallelt emne, dekker Strenger, f-strenger og formatert utskrift stoffet der.

Ett ord før vi begynner: å formatere betyr her bare å bestemme hvordan en verdi skal se ut når den skrives — hvor bred kolonnen er, hvor mange desimaler tallet får, og om det fylles med nuller foran. Verdien selv endres ikke.

Løkke 1 — det print gjør uten at du ber om det (~10 min)

Kiosken på en fotballkamp fører salget sitt i to lister: hva som ble solgt, og hvor mange. Skal tallene på skjermen bli lesbare, må du først vite nøyaktig hva print gjør med det du gir den — for den gjør to ting av seg selv.

Den setter ett mellomrom mellom argumentene, og den avslutter med ett linjeskift. Begge kan du overstyre, og begge er faste feller i kodesporing.

`print(a, b, c)`

Skriver ut argumentene etter hverandre på én linje, med ett mellomrom mellom hvert av dem, og avslutter med linjeskift. Argumentene kan være av hvilken som helst type — print gjør dem om til tekst selv, så print(42) virker uten str().

Mellomrommet er ikke noe du har bedt om, og det er ikke det samme som å skjøte med +: print('kr', 42) gir kr 42, mens print('kr' + str(42)) gir kr42.

`print(..., sep=teksten)`

Bytter ut mellomrommet mellom argumentene med den teksten du oppgir. sep er kort for separator, altså skilletegnet.

print('a', 'b', sep=';') gir a;b, og print('a', 'b', sep='') gir ab uten noe imellom. sep har ingen virkning når print bare får ett argument.

`print(..., end=teksten)`

Bytter ut linjeskiftet på slutten med den teksten du oppgir. end er ordet for slutt.

print('a', end='') skriver a og blir stående på samme linje, så neste print fortsetter der. print('a', end=' ') gir ett mellomrom i stedet for linjeskiftet. Dette er måten du skriver ei hel rad med tall på én linje inne i en løkke.

`print()` uten argumenter

Skriver ingen tegn, bare linjeskiftet. Resultatet er en tom linje i utskriften.

Den brukes til luft mellom avsnitt i en rapport, og den er lett å overse når du sporer kode: en tom linje i fasiten er like mye et poeng som en linje med tekst i.

✏️Eksempel 1: fire måter å skrive den samme salgslinja på

Kiosken har solgt 42 pølser. Skriv ut vare og antall på fire måter: vanlig, med kolon imellom, delt over to print-kall som havner på samme linje, og med en tom linje etter.

De tre første linjene bruker print på hver sin måte; den fjerde viser hva et bart print() gjør.

vare = 'pølse'
solgt = 42
print(vare, solgt)
print(vare, solgt, sep=': ')
print(vare, end=' ')
print(solgt)
print()
print('ferdig')

Utskrift:

pølse 42
pølse: 42
pølse 42

ferdig

Legg merke til at linje 3 og 4 i koden gir én linje i utskriften: end=' ' fjernet linjeskiftet og satte et mellomrom i stedet, så neste print fortsatte på samme linje. Og det er en ekte tom linje mellom pølse 42 og ferdig — den kommer fra print().

📝Oppgave 1

(Innstegsoppgave, sjanger B — kodesporing, altså «hva skriver dette programmet ut?».) Skriv utskriften nøyaktig, linje for linje. Husk at sep styrer det som står mellom argumentene, og end det som står etter det siste.

print('A', 'B', 'C')
print('A', 'B', 'C', sep='')
print('A', end='')
print('B')

Løkke 2 — kolonner med fast bredde (~10 min)

Kioskrapporten er lesbar først når tallene står under hverandre. Trikset er å gi hvert felt en fast bredde: teksten fylles ut med mellomrom til den er akkurat så bred, og da havner neste felt på samme sted i hver rad.

Tre strengmetoder gjør dette. De virker bare på tekst, så tall må gjennom str() først.

`s.ljust(n)`

Gir en ny streng der s står til venstre og fylles ut med mellomrom til lengden er n. Left justify betyr venstrejuster.

'kakao'.ljust(8) gir 'kakao ' — fem tegn pluss tre mellomrom. Er s allerede minst n tegn lang, gjøres ingenting: strengen blir aldri klippet. Originalen er uendret.

`s.rjust(n)`

Gir en ny streng der s står til høyre og fylles ut med mellomrom foran til lengden er n. Right justify betyr høyrejuster.

'42'.rjust(5) gir ' 42'. Tall skal alltid høyrejusteres i en tabell, slik at enerne står under enerne og tierne under tierne.

`s.center(n)`

Gir en ny streng der s står midt i et felt på n tegn, med mellomrom fordelt på begge sider. Går det ikke opp, får høyresiden det ekstra mellomrommet.

'Antall'.center(10) gir ' Antall '. Metoden brukes nesten bare på overskriftsraden.

`str(x)` før justering

Justeringsmetodene er strengmetoder, så de finnes ikke på et tall. 42.rjust(5) er en syntaksfeil, og antall.rjust(5) gir AttributeError når antall er et heltall.

Løsningen er å konvertere først: str(antall).rjust(5). Dette er den motsatte veien av int() etter innlesing — inn i programmet konverterer du til tall, ut av programmet konverterer du til tekst.

✏️Eksempel 2: kioskrapporten i rette kolonner

Kiosken har solgt 42 pølser, 17 kakao og 8 vafler. Skriv rapporten med varenavnet i et felt på 8 tegn til venstre og antallet i et felt på 4 tegn til høyre, med en overskriftsrad over.

Overskriften bygges med de samme breddene, ellers står den ikke over kolonnene sine.

varer = ['pølse', 'kakao', 'vaffel']
antall = [42, 17, 8]
print('Vare'.ljust(8) + 'Ant'.rjust(4))
for i in range(len(varer)):
    print(varer[i].ljust(8) + str(antall[i]).rjust(4))

Utskrift:

Vare     Ant
pølse     42
kakao     17
vaffel     8

Tallene står under hverandre fordi hvert av dem fyller nøyaktig fire tegn: 42, 17 og 8. Hadde du skrevet print(varer[i], antall[i]) i stedet, ville kolonnen sprikt, fordi varenavnene er ulikt lange.

Legg merke til str(antall[i]). Uten den stopper programmet, fordi .rjust ikke finnes på et heltall.

📝Oppgave 2
Sjanger I

En fergeavgang har solgt billetter i tre klasser. Navnene ligger i klasser og antallet i solgte, i samme rekkefølge.

a) Skriv funksjonen skriv_billettliste(klasser, solgte) som skriver en overskriftsrad og deretter én linje per klasse. Klassenavnet skal stå venstrejustert i et felt på 10 tegn, antallet høyrejustert i et felt på 5.
b) Legg til en sluttlinje som skriver totalen i det samme oppsettet, med teksten SUM i navnefeltet.

Test med klasser = ['voksen', 'barn', 'kjøretøy'] og solgte = [128, 47, 63].

📝Oppgave 3
Sjanger B

Hva skriver programmet ut? Skriv begge linjene nøyaktig, med bindestrekene og mellomrommene der de faktisk står.

tall = [3, 8, 12, 7]
for x in tall:
    print(x, end='-')
print()
for i in range(0, len(tall), 2):
    print(tall[i], end=' ')
print('slutt')

Løkke 3 — desimaler og feltbredde i ett grep (~12 min)

Så snart kroner kommer inn i rapporten, holder det ikke å justere. print(467.5) gir 467.5, ikke 467.50, og en utregnet pris kan komme ut som 31.166666666666668. Du må kunne bestemme antall desimaler.

Til det finnes en egen liten formatoppskrift som både setter bredden og antall desimaler. Den skrives på to måter: den eldre med .format(), som arkivet bruker, og den nyere med f-streng. Oppskriften inni er den samme.

`'{} og {}'.format(a, b)`

Krøllparentesene {} i strengen er plassholdere: .format() setter argumentene sine inn i dem, i rekkefølge, og gjør dem om til tekst underveis.

'{} kr for {}'.format(27, 'kakao') gir '27 kr for kakao'. Antall plassholdere skal stemme med antall argumenter. Resultatet er en vanlig streng, så den kan lagres i en variabel og skrives ut senere.

Formatoppskriften `{:8.2f}`

Alt etter kolonet inne i krøllparentesen er en oppskrift for hvordan verdien skal se ut. {:8.2f} betyr: bredde 8 tegn, 2 desimaler, og f for fixed point, altså vanlig desimaltall.

'{:8.2f}'.format(467.5) gir ' 467.50' — åtte tegn i alt, med to desimaler og mellomrom foran. Desimaltall høyrejusteres av seg selv. Vil du bare ha desimalene og ingen fast bredde, skriver du {:.2f}. Merk at verdien avrundes, den klippes ikke: 2,675 blir 2.68, og desimalskilletegnet er punktum, ikke komma.

Justering i oppskriften: `{:<10}` og `{:>6}`
< betyr venstrejustert, > høyrejustert og ^ midtstilt, etterfulgt av bredden. '{:<10}'.format('kakao') gir det samme som 'kakao'.ljust(10).

Fordelen er at du slipper str(): oppskriften konverterer selv, så '{:>6}'.format(42) virker direkte på et heltall. Tekst venstrejusteres av seg selv, tall høyrejusteres av seg selv — pilene trengs bare når du vil ha det motsatte.

f-strengen `f'{verdi:8.2f}'`

Setter du bokstaven f rett foran anførselstegnet, kan du skrive uttrykk direkte inne i krøllparentesene, med den samme oppskriften etter kolonet.

f'{pris:8.2f}' gir nøyaktig det samme som '{:8.2f}'.format(pris), bare kortere, og du ser hvilken variabel som havner hvor. Uttrykk er lov: f'{a + b:.1f}'.

Merk: f-strenger er (verifiser mot gjeldende pensum — varslet inn 2019); arkivet 2014–2019 bruker .format() og .rjust(). Begge former er gyldige på eksamen, og boka viser dem side om side. Bokstaven f foran fnutten har ingenting å gjøre med f-en inne i oppskriften — den første betyr format, den andre betyr desimaltall.

✏️Eksempel 3: samme rad skrevet på tre måter

Kiosken solgte 17 kakao til 27,50 kroner stykket. Skriv raden «kakao, antall, sum i kroner» med varenavnet i et felt på 8 tegn, antallet i 3 og summen i 9 med to desimaler — først med justeringsmetodene, så med .format(), så med f-streng.

De tre variantene skal gi tre helt like linjer. Det er poenget.

vare = 'kakao'
antall = 17
sum_kr = antall * 27.5
print(vare.ljust(8) + str(antall).rjust(3) + '{:.2f}'.format(sum_kr).rjust(9))
print('{:<8}{:>3}{:>9.2f}'.format(vare, antall, sum_kr))
print(f'{vare:<8}{antall:>3}{sum_kr:>9.2f}')

Utskrift:

kakao    17   467.50
kakao    17   467.50
kakao    17   467.50

Tre identiske linjer, tre skrivemåter. Alle tre gir full pott. Den første er den mest omstendelige, fordi den må konvertere med str() og formatere desimalene i et eget steg. Den andre og tredje gjør begge deler i ett.

Legg merke til at 17 * 27.5 blir 467.5 og ikke 467.50 — nullen kommer først når du ber om to desimaler. Uten formatering hadde en pris som 31.166666666666668 gått rett ut i rapporten.

📝Oppgave 4
Eksamensnivå, sjanger B

Hva skriver programmet ut? Skriv alle fire linjene nøyaktig, med mellomrommene. Den loddrette streken er med for at du skal se hvor hvert felt slutter.

sted = 'Ås'
pris = 4.567
print(sted.rjust(6) + '|')
print('{:8.2f}'.format(pris) + '|')
print(f'{sted:<6}|')
print(f'{pris:.1f}' + '|')

Løkke 4 — nullpadding og tidsformat (~8 min)

Et svømmestevne måler tiden i sekunder, men resultatlista skal vise 01:02:05. Her holder det ikke å høyrejustere: feltene skal fylles med nuller, ikke mellomrom, og de skal alltid være to sifre.

Det kalles nullpadding, og det finnes to skrivemåter — som vanlig én eldre og én nyere.

`str(t).zfill(n)`

Fyller strengen med nuller foran til lengden er n. Navnet er kort for zero fill.

str(5).zfill(2) gir '05', og str(42).zfill(2) gir '42' — er strengen allerede lang nok, gjøres ingenting. Metoden er en strengmetode, så tallet må gjennom str() først. Dette er formen arkivets løsningsforslag bruker.

`f'{t:02d}'` og `'{:02d}'.format(t)`

Den samme nullpaddingen skrevet som en formatoppskrift: 0 betyr fyll med nuller, 2 er bredden, og d betyr heltall (decimal).

f'{5:02d}' gir '05'. Fordelen er at den tar heltallet direkte, uten str(). For desimaltall er den tilsvarende f'{x:06.2f}', som gir '004.57' — nuller foran, to desimaler, seks tegn i alt.

📜Kodemønster: sekunder til `hh:mm:ss`

Fra et antall sekunder til tidsformat er det tre utregninger og én sammensetting. Mønsteret er alltid det samme, og det er verdt å kunne utenat:

1. Timer: sek // 3600 — heltallsdeling, fordi 3 600 sekunder er én time.
2. Minutter: (sek % 3600) // 60 — først resten etter hele timer, så hele minutter av den.
3. Sekunder: sek % 60 — resten etter hele minutter.
4. Sett sammen med kolon, og nullpadd hvert felt til to sifre.

Merk at alle tre stegene bruker // og %, aldri /. Vanlig deling gir desimaltall, og da kommer det ut 1.0343055555555556 i stedet for 1.

Skal formatet være mm:ss i stedet, faller første steg bort: minutter er sek // 60 og sekunder er sek % 60.

✏️Eksempel 4: fra sekunder til klokkeformat

Skriv formater_tid(sek) som gjør et antall sekunder om til en streng på formen hh:mm:ss. Test med 3 725 sekunder, 59 sekunder og 36 000 sekunder.

Først med zfill, slik arkivets løsningsforslag skriver det:

def formater_tid(sek):
    timer = sek // 3600
    minutter = (sek % 3600) // 60
    sekunder = sek % 60
    return str(timer).zfill(2) + ':' + str(minutter).zfill(2) + ':' + str(sekunder).zfill(2)

print(formater_tid(3725))
print(formater_tid(59))
print(formater_tid(36000))

Utskrift:

01:02:05
00:00:59
10:00:00

Regnestykket for 3 725: 3725 // 3600 er 1 time, resten er 125 sekunder, 125 // 60 er 2 minutter, og 125 % 60 er 5 sekunder — altså 1 time, 2 minutter og 5 sekunder.

Den samme funksjonen med f-streng blir tre linjer kortere:

def formater_tid(sek):
    return f'{sek // 3600:02d}:{sek % 3600 // 60:02d}:{sek % 60:02d}'

print(formater_tid(3725))
print(formater_tid(59))
print(formater_tid(36000))

Utskrift:

01:02:05
00:00:59
10:00:00

Begge gir full pott. Den første er lettere å feilsøke, fordi hvert mellomsteg har et navn; den andre er kortere å skrive for hånd. Velg den du husker uten å nøle.

📝Oppgave 5
Sjanger I

Et svømmestevne måler heat-tider i hele sekunder.

a) Skriv formater_mmss(sek) som gir tiden på formen mm:ss med to sifre i hvert felt, for eksempel 143 sekunder til 02:23.
b) Skriv skriv_resultater(navn, tider) som skriver én linje per svømmer med navnet venstrejustert i et felt på 12 tegn og tiden etter. Funksjonen skal gjenbruke formater_mmss.

Test med navn = ['Iversen', 'Lund', 'Bakkehaug'] og tider = [143, 97, 205].

📝Oppgave 6
Eksamensnivå, sjanger I

En sykkelklubb har logget siste treningsrunde. Navnene ligger i ryttere, tiden i hele sekunder i tider og distansen i kilometer i km, alle i samme rekkefølge.

a) Skriv snittfart(km, sek) som returnerer farten i kilometer i timen.
b) Skriv skriv_runderapport(ryttere, tider, km) som skriver en overskriftsrad og deretter én linje per rytter: navnet venstrejustert i 12 tegn, tiden som hh:mm:ss, og farten høyrejustert i et felt på 8 tegn med én desimal.
c) Forklar med én setning hvorfor farten må formateres, og ikke bare skrives rett ut.

Test med ryttere = ['Aarnes', 'Sivertsen', 'Holt'], tider = [4520, 3980, 5310] og km = [42.5, 38.0, 47.2].

Repetisjon — de seks setningene du bør ha med deg
API- og konstruksjonsliste

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 Norges teknisk-naturvitenskapelige universitet. Dette er ikke offisielt studiemateriell. Les mer.