Når man skriver kommandolinjeprogrammer, er det viktig å forstå hvordan programmet ditt kommuniserer med brukeren gjennom utdata. Et godt strukturert program skiller klart mellom standard utdata (STDOUT) og feilmeldinger (STDERR). Dette er en praksis som gjør programmet mer robust og lettere å feilsøke. For eksempel, når et program får feil i kommandoargumenter, bør det skrive feilmeldingene til STDERR og ikke forstyrre den vanlige prosessen i STDOUT.
En god kommando-linjeapplikasjon bør også håndtere feilmeldinger på en smart måte. I Rust, når et program trenger å håndtere feil som ikke nødvendigvis skal stoppe programmet, kan du skrive advarsler til STDERR og deretter fortsette prosessen. Et godt eksempel på dette finnes i kapittel 3, hvor vi skriver et program som prosesserer filargumenter, noen av dem er kanskje manglende eller uleste. I slike tilfeller kan du bruke STDERR til å logge advarsler, mens programmet fortsetter å jobbe med de andre filene uten å stoppe helt.
For å håndtere argumentene i programmet vårt, bruker vi Rusts clap-bibliotek som tillater oss å enkelt analysere kommando-linjeargumenter. Et vanlig scenario er å hente ut en tekststreng som kan bestå av flere ord. For å håndtere dette i Rust, kan man bruke en Vec<String>, som er en dynamisk array-type som kan vokse og endre størrelse under programkjøringen. For å hente ut flere verdier for et argument, benyttes funksjonen get_many fra ArgMatches. Denne funksjonen returnerer en Option, som enten er None (hvis ingen verdi er funnet) eller Some (hvis en verdi eksisterer). Siden vi vet at minst én verdi alltid vil være tilgjengelig, kan vi trygt bruke unwrap for å hente ut verdiene.
Et viktig aspekt her er forskjellen på operasjonene copy og clone i Rust. I Rust behandles forskjellige datatyper ulikt når det gjelder minnehåndtering. Enkelte typer, som tall, kan kopieres direkte på stakken (en type minne som er rask og har en fast størrelse), mens andre typer, som strenger, ligger på haugen og må klones for å unngå problemer med datadeling. Dette er et viktig konsept i Rusts minnehåndteringssystem, som skiller seg fra dynamiske språk som skjuler disse detaljene fra utvikleren.
Når vi bruker en Vec<String>, som representerer en liste med tekststrenger, kan vi bruke funksjonen Vec::join for å sammenkoble strengene til én enkelt streng, separert med et mellomrom. Denne sammenkoblede strengen kan så skrives ut til STDOUT ved hjelp av println!-makroen, som automatisk legger til et linjeskift etter teksten. I tilfelle vi har et argument som styrer om det skal være linjeskift eller ikke (for eksempel et flagg som heter -n), kan vi bruke print!-makroen, som ikke legger til et linjeskift med mindre vi eksplisitt gir det.
En vanlig utfordring i Rust, som kan oppstå når man prøver å håndtere variabler, er hvordan man skal endre verdier i et program. I Rust er variabler immutabel som standard, som betyr at man ikke kan endre verdiene etter at de er tildelt. Hvis du ønsker å endre en variabel, som for eksempel i tilfelle av linjeskift, må du erklære variabelen som mut (mutable). Hvis dette ikke gjøres, vil kompilatoren gi en feilmelding om at variabelen ikke kan tildeles på nytt.
En viktig detalj som kan overses, er at Vec<String> krever at alle elementene er av samme type. Dette er et typisk eksempel på hvordan Rust gir strengere typekontroll enn mange andre språk, og dette hjelper til med å forhindre feil tidlig i utviklingsprosessen.
En annen utfordring som kan oppstå når du skriver programmer som skal håndtere brukerinput, er hvordan du skal gjøre programmer mer robuste når de ikke mottar forventet input. I stedet for at programmet krasjer eller stopper helt, kan du bruke Rusts feilhåndteringsmekanismer for å logge feil eller advarsler til STDERR. Dette tillater at programmet kan fortsette med andre operasjoner, selv om enkelte feil oppstår.
En god forståelse av hvordan argumenter behandles, hvordan minne håndteres, og hvordan feilmeldinger skilles fra normal utdata, er essensiell når du skriver Rust-programmer. Rust gir deg verktøyene du trenger for å håndtere komplekse situasjoner på en trygg og effektiv måte, men det krever at du er bevisst på detaljer som forskjellen mellom stakk og haug, bruk av copy og clone, og hvordan du korrekt bruker mut for å gjøre variabler endringsbare. Dette er alt en del av Rusts filosofi om minnesikkerhet og effektivitet, som setter det fra andre programmeringsspråk.
Hvordan lese filer med forskjellige kodingsformater og linjeskift?
Å håndtere filer i programmering kan være langt mer komplisert enn man skulle tro, spesielt når det gjelder håndtering av forskjellige tekstkodingsformater og linjeskiftstiler. Dette kapittelet fokuserer på hvordan man kan lese filer med forskjellige kodingsformater, linjeskift og bytehåndtering. Når vi arbeider med tekstfiler, er det avgjørende å forstå forskjellen mellom å lese filer som byte-strenger og som karakter-strenger.
I de tidlige 1960-årene ble ASCII (American Standard Code for Information Interchange) etablert som en standard for tekstkoding, og denne kodingstabellen besto av 128 tegn. ASCII-bruken krevde bare 7 biter per tegn, og en byte – som består av 8 biter – var tilstrekkelig til å representere disse 128 tegnene. På den tiden kunne begrepet «byte» og «tegn» brukes om hverandre.
Men med utviklingen av Unicode, som har som mål å representere alle verdens skriftsystemer (og til og med emojis), måtte vi utvide begrepet byte. Noen Unicode-tegn kan kreve opptil 4 byte. En av de mest brukte kodene i dag er UTF-8, som bruker 8 biter for å representere tegn. Utforskningen av filene vi jobber med, avslører noen interessante forskjeller som det er viktig å forstå.
For eksempel, filen tests/inputs/one.txt begynner med symbolet Ő, som er to byte langt i UTF-8. Hvis vi prøver å hente kun ett byte, får vi ikke et gyldig tegn, men en feilindikator, som representerer et problem med å konvertere et tegn til Unicode. Dette kan illustreres med følgende kommando:
Utfordringen som programmet vårt skal løse, er å håndtere slike situasjoner ved å lese og vise tegn korrekt.
Når det gjelder linjeskift, er det en annen viktig detalj. I Windows er linjeskiftet kombinert av både «carriage return» (CR) og «line feed» (LF), ofte representert som CRLF eller \r\n. På Unix-baserte systemer er linjeskiftet kun LF, eller \n. Dette er noe programmet ditt må kunne håndtere korrekt. Hvis du for eksempel bruker funksjonen BufRead::lines til å lese fra en fil, vil den automatisk fjerne linjeskiftene. Dette kan føre til at Windows-stil linjeskift ikke blir bevart, noe som kan føre til feil i utdataene dine.
For å bevare linjeskiftene på riktig måte, bør du bruke en metode som ikke fjerner linjeskiftene automatisk, som BufRead::read_line. Denne funksjonen leser byte etter byte til den finner linjeskifttegnene (0xA for LF eller 0xD, 0xA for CRLF), og deretter legger den disse til den lesede strengen. Dette kan oppnås med følgende kode:
I dette eksemplet åpner vi filen, og deretter leser vi hver linje med read_line som bevarer linjeskiftet. Vi bruker en iterasjon for å lese et spesifisert antall linjer, og hver linje skrives ut med linjeskiftet intakt.
Neste steg er å håndtere lesing av byte-strenger fra filene. Dette er spesielt viktig når du trenger å lese en bestemt mengde byte fra filen. For eksempel, hvis du vil lese et spesifikt antall byte, kan du opprette en buffer og lese inn dataene i den. Når du har lest byte, kan du konvertere dem til en streng, selv om det kanskje ikke er et gyldig UTF-8-tegn for hele bufferet. Dette kan illustreres med følgende kode:
Her har vi et mønster som leser et spesifisert antall byte og konverterer disse til en streng ved hjelp av String::from_utf8_lossy, som håndterer ugyldige byte.
Det er viktig å merke seg at selv om Rust gir oss kraftige verktøy for å håndtere filinnlesing, er det alltid utfordringer knyttet til filens kodingsformat og linjeskift. Når man skriver et program som leser fra forskjellige kilder og plattformer, må man være ekstra oppmerksom på hvordan filene ble kodet, og hvordan programmet ditt håndterer byte-strenger versus tegn-strenger.
Husk også at ved lesing av store filer eller flere filer samtidig, kan programmet ditt møte på problemer relatert til effektiv ressursbruk og feilbehandling. Det er derfor viktig å implementere tilstrekkelig feilhåndtering for å unngå at programmet krasjer på grunn av uventede formater.

Deutsch
Francais
Nederlands
Svenska
Norsk
Dansk
Suomi
Espanol
Italiano
Portugues
Magyar
Polski
Cestina
Русский