formater, ikke konkatiner

Mye av det programmene våre gjør er å behandle tekst (og da mener jeg “våre” som i “oss utviklere og alt vi finner på å lage”). Noe av det første vi lærer som ferske hackere er å konkatinere strenger – dvs. å slå sammen to biter med tekst.

Og i forhold til konstruksjon av tekststrenger stopper det der for veldig mange. Du kan egentlig klare deg ganske lenge med enkel konkatinering, men det er ikke alltid det mest elegante!

Siden tidenes morgen (dvs. 50-tallet) har programmeringsspråk inkludert diverse metoder for å formatere tekst. Den aller mest kjente og brukte funksjonen heter printf, og dukket først opp i språket C – men den ble egentlig arvet fra BCPL, hvor den het writef (men hvem har vel egentlig hørt om BCPL?). Siden har printf inspirert andre, og i dag finner du dens etterkommere i nær sagt alle programmeringsspråk.

Format i .Net

Ett av disse språkene er C#, hvor vi kan bruke formatstrenger i mange sammenhenger. Den vanligste bruken er en statisk metode på String-klassen som heter Format. Her er et banalt eksempel:

10 int age = 35;
11 string givenName = "Torbjørn";
12 string surname = "Marø";
13 
14 // Konkatinering på den vanlige måten
15 
16 string displayName = surname + ", " + givenName + " (" + age + ")";
17 
18 // mer elegant med en formatstreng
19 
20 string displayName = String.Format("{0}, {1} ({2})",
21                                    surname,
22                                    givenName,
23                                    age);

Det er mye lettere å innføre feil ved vanlig konkatinering enn ved bruk av format, og det er også (i alle fall for et trent øye) mye lettere å se hva resultatet kommer til å bli når man ser en formatstreng.

String.Format er mye mer avansert enn det kan virke ved første øyekast. Men det er sjelden vi benytt mer enn det mest elementære, og jeg må nesten alltid slå opp i dokumentasjonen om jeg trenger å formatere f.eks. tall eller en dato på en bestemt måte.

I det neste eksempelet bruker jeg Console.Writeline, som også aksepterer en formatstreng. Her har jeg spesifisert at tallet skal formateres som penger, og at datoen skal vises som dagens navn, dagens dato (med ett siffer om mindre enn 10) og månedens navn.

1 double money = 12345.6789;
2 Console.WriteLine("Pris ({0}): {1:C} | {2:dddd dd. MMMM}",
3                   CultureInfo.CurrentCulture,
4                   money,
5                   DateTime.Now);
6 
7 // output: "Pris (nb-NO): kr 12 345,68 | torsdag 7. april"

Format i Clojure (og Java)

I Clojure har vi også en format-funksjon, og en tilsvarende printf-funksjon som tar en formatstreng som input og skriver til standard out. Under panseret bruker disse funksjonene java.util.Formatter, som er inspirert av C’s tradisjonelle printf.

Her er noen eksempler på format i bruk, og strengene de genererer..

10 (format "Hello, %s!" "world")                      ;=> Hello, world!
11 (format "%s = %s" "SomeKey" "SomeValue")           ;=> SomeKey = SomeValue
12 (format "%1$s + %1$s = %2$s" 2 4)                  ;=> 2 + 2 = 4
13 (format "e = %+.4f" 2.718281828459045)             ;=> e = +2,7183
14 (format ":%-20s:" "right padding")                 ;=> :right padding       :
15 (format ":%20s:" "left padding")                   ;=> :        left padding:
16 (format "%b %b %b %b" true false nil "something")  ;=> true false false true
17 (format "#%02x%02x%02x;" 255 0 125)                ;=> #ff007d;
18 (format "%s" (Date.))                              ;=> Thu Apr 07 21:04:29 CEST 2011
19 (format "Offset: %tz" (Date.))                     ;=> Offset: +0100
20 (format "Time zone: %tZ" (Date.))                  ;=> Time zone: CEST
21 (format "%1$tH:%1$tM:%1$tS" (Date.))               ;=> 21:04:29
22 (format "%1$tl:%1$tM %1$tp" (Date.))               ;=> 9:04 pm
23 (format "Unix time: %ts" (Date.))                  ;=> Unix time: 1302203069
24 (format "%1$td/%1$tm-%1$ty" (Date.))               ;=> 07/04-11
25 (format "%1$td. %1$tb %1$ty" (Date.))              ;=> 07. apr 11
26 (format "%1$td. %1$tB %1$tY" (Date.))              ;=> 07. april 2011
27 (format "ISO 8601: %tF" (Date.))                   ;=> ISO 8601: 2011-04-07

(Dette eksempelet ble generert av et Clojure-skript. Hvis du er interesert finner du det her.)

Interpolering

Variabel-interpolering er en lignende teknikk man finner i mange dynamiske språk, som Perl, PHP og Ruby. Her kan tekst-strenger direkte inneholde referanser til variabler eller mer komplekse uttrykk, og språket parser og evaluerer dem og erstatter dem med teksten.

Her er et lite eksempel fra Ruby..

1 given = 'James'
2 surname = 'Bond'
3 
4 puts "My name is #{surname}, #{given} #{surname}."
5 
6 # => My name is Bond, James Bond.

Clojure har også en interpolerings-funksjon i contrib-bibloteket. Den heter <<, og i bruk ser det slik ut:

 1 (ns demo (:use clojure.contrib.strint))
 2 
 3 (let [given "James" surname "Bond"]
 4   (println (<< "My name is ~{surname}, ~{given} ~{surname}.")))
 5 
 6 ; => My name is Bond, James Bond.
 7 
 8 (let [numbers '(1 2 5 9 4 4)]
 9   (println (<< "The sum of ~{numbers} is ~(apply + numbers).")))
10 
11 ; => The sum of (1 2 5 9 4 4) is 25.

Om du trodde regex var uforståelig…

De mest avanserte formateringsmulighetene jeg har sett er dem man har i Common Lisp. Format-strengene kan inneholdet sitt helt eget mini-språk, og hvis du synes at regex er komplisert og uforståelig så vent bare til du ser dette. Formatene kan for eksempel ha kontrollflyt, løkker og rekursjon!

Igjen har clojure en implementasjon av det samme, og den heter cl-format. Dette er bare en liten smakebit..

10 (ns demo (:use clojure.contrib.pprint))
11 
12 (defn print-dogs [n]
13   (cl-format true "~R dog~:[s are~; is~] here." n (= 1 n)))
14 
15 (print-dogs 1) ; => one dog is here.
16 (print-dogs 3) ; => three dogs are here.
17 
18 (cl-format true "Par:~{ <~S,~S>~}" '(A 1 B 2 C 3))
19 
20 ; => Par: <A,1> <B,2> <C,3>

Vil du vite mer om mulighetene kan du ta en titt i Common Lisp HyperSpec (som er den styggeste API-dokumentasjonen jeg noen sinne har sett).

Oppsummering

Den siste tiden, spesielt gjennom å studere Clojure, har jeg liksom gjennoppdaget printf. Når jeg husker på å benytte meg av de kraftige formateringsmulighetene resulterer det ofte i mer elegant kode. Konkatinering er clunky, bruk format!

Kategorier: LISP/Clojure, Polyglot.
RSS feed for kommentarene. Tilbaketråkk.

2 kommentarer til “formater, ikke konkatiner”

  1. Erik Brandstadmoen Says:

    Helt enig i artikkelens innhold.

    To småkommentarer:
    1) Det heter konkatEnere på norsk (og concatEnate på engelsk)
    2) MS-DOS har også ganske eksotiske format-stringer. Tror noen i Lisp-verdenen har snakket med noen i MS-DOS-verdenen. Se f.eks. http://www.dostips.com/DtTipsStringManipulation.php

    Ellers er selvsagt String.Format’s fortreffelighet og merkelige muligheter altfor lite verdsatt .

  2. Torbjørn Says:

    Konkatinere, konkatenere – det viktige er ikke hva det heter, men hva folk skriver inn i google for å finne det de leter etter ;) Takk for kommentaren!

Skriv en kommentar

Tillatte tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>


Alf Kåre Lefdal: Distributed Podcast er også ganske interessant. De tar opp tema som fx. ...

Stian: +1 for 6er til This Developer's Life! Min definitive favoritt. Jeg trengte også...

Torbjørn: Takk for flere tips, Vegard. Deep Fried Bytes ligger på oversikten min fra 2009...

Vegar: Og glemte helt ios: Nsbrief og ideveloper live. Har du hørt på deep fried byt...

Vegar: Mye kjekt her. TDL, hanselminutes og .net rocks ligger i en klasse for seg. Suv...

Torbjørn: Helt enig, arkivet til Software Engineering Radio er en gullgruve om man vet hva...

Einar W. Høst: Jeg synes at det kuleste med se-radio er backloggen av intervjuer... det er noen...

arnab: fantastisk :)...

Olav: Glimrende blogg ! Modellen av hjernens arbeid passer ikke bare på nyskaping: ...

Torbjørn: Ja, flydesign trekkes ofte frem som et eksempel på dette fenomenet. Design av b...

Mulig relaterte linker

 Hold deg oppdatert

Søk i bloggen

Ferske innlegg

  • NodeJS vs. ASP.NET
  • Pulten min..
  • No ifs and buts
  • Community-fiskebolle på ROOTS 2012
  • Kategorier

  • .net ninja (37)
  • Bøker (18)
  • Diverse prosjekter (37)
  • DSL (10)
  • Erlang (10)
  • F# (5)
  • Hardware (1)
  • Jobb (78)
  • Julekalender (51)
  • kjempekjekt.com (23)
  • LISP/Clojure (34)
  • NDC (4)
  • NNUG / community (63)
  • O/RM & databaser (10)
  • Off topic (118)
  • OO-design/clean code (31)
  • Podcasts (15)
  • Polyglot (82)
  • Ruby (29)
  • Silverlight / RIA (3)
  • Software/verktøy (20)
  • Softwareutvikling (24)
  • Testing / TDD (30)
  • the contiki strip (13)
  • User experience (3)
  • WCF (3)
  • Webutvikling (34)
  • WPF (9)
  • WTF (13)
  • Last ned Wallpaper

    Programmeringsbloggens tøffe skrivebordsbakgrunn med snippets fra ulike språk laster du ned her!

    Abonner via epost

    Om du vil kan du få alle nye blogposter tilsendt til din epost. Abonner nå, det er kjempeenkelt!

    Meta