C Programmeringsveiledning om håndtering av tilfeldig tilgang

Folk som kommuniserer kryptert data ved hjelp av cloud computing

Roy Scott / Getty Images





Bortsett fra de enkleste programmene, må de fleste programmer lese eller skrive filer. Det kan være bare for å lese en konfigurasjonsfil, eller en tekstparser eller noe mer sofistikert. Denne opplæringen fokuserer på å bruke tilfeldige tilgangsfiler i C.

Programmering av Random Access File I/O i C

binær fil

D3Damon/Getty Images



De grunnleggende filoperasjonene er:

  • fopen - åpne en fil - spesifiser hvordan den skal åpnes (les/skriv) og skriv (binær/tekst)
  • fclose - lukk en åpnet fil
  • fread - les fra en fil
  • fwrite - skriv til en fil
  • fseek/fsetpos - flytt en filpeker til et sted i en fil
  • ftell/fgetpos - forteller deg hvor filpekeren er plassert

De to grunnleggende filtypene er tekst og binær. Av disse to er binære filer vanligvis enklere å håndtere. Av den grunn og det faktum at tilfeldig tilgang på en tekstfil ikke er noe du trenger å gjøre ofte, er denne opplæringen begrenset til binære filer. De fire første operasjonene som er oppført ovenfor er for både tekstfiler og filer med tilfeldig tilgang. De to siste bare for tilfeldig tilgang.



Tilfeldig tilgang betyr at du kan flytte til hvilken som helst del av en fil og lese eller skrive data fra den uten å måtte lese gjennom hele filen. For mange år siden ble data lagret på store spoler med databånd. Den eneste måten å komme til et punkt på båndet var ved å lese hele veien gjennom båndet. Så kom disker og nå kan du lese hvilken som helst del av en fil direkte.

Programmering med binære filer

En binær fil er en fil av hvilken som helst lengde som inneholder byte med verdier i området 0 til 255. Disse bytene har ingen annen betydning i motsetning til i en tekstfil der en verdi på 13 betyr vognretur, 10 betyr linjeskift og 26 betyr slutten av fil. Programvare som leser tekstfiler må forholde seg til disse andre betydningene.

Binære filer er en strøm av byte, og moderne språk har en tendens til å fungere med strømmer i stedet for filer. Den viktige delen er datastrømmen i stedet for hvor den kom fra. IC, kan du tenke på dataene enten som filer eller strømmer. Med tilfeldig tilgang kan du lese eller skrive til hvilken som helst del av filen eller strømmen. Med sekvensiell tilgang må du gå gjennom filen eller streame fra starten som et stort bånd.

Dette kodeeksemplet viser en enkel binær fil som åpnes for skriving, med en tekststreng (char *) som skrives inn i den. Normalt ser du dette med en tekstfil, men du kan skrive tekst til en binær fil.



Dette eksemplet åpner en binær fil for skriving og skriver deretter en char * (streng) inn i den. Variabelen FILE * returneres fra fopen()-kallet. Hvis dette mislykkes (filen kan eksistere og være åpen eller skrivebeskyttet, eller det kan være en feil med filnavnet), returnerer den 0.

Kommandoen fopen() prøver å åpne den angitte filen. I dette tilfellet er det test.txt i samme mappe som programmet. Hvis filen inkluderer en bane, må alle omvendte skråstreker dobles opp. 'c:mappe est.txt' er feil; du må bruke 'c:\mappe\test.txt'.



Siden filmodusen er 'wb', skriver denne koden til en binær fil. Filen opprettes hvis den ikke eksisterer, og hvis den gjør det, slettes det som var i den. Hvis kallet til fopen mislykkes, kanskje fordi filen var åpen eller navnet inneholder ugyldige tegn eller en ugyldig bane, returnerer fopen verdien 0.

Selv om du bare kan sjekke at ft ikke er null (suksess), har dette eksemplet en FileSuccess()-funksjon for å gjøre dette eksplisitt. På Windows sender den ut suksessen/mislykket anropet og filnavnet. Det er litt tyngende hvis du er ute etter ytelse, så du kan begrense dette til feilsøking. På Windows er det lite overhead som sender ut tekst til systemfeilsøkeren.



Fwrite()-kallene gir ut den angitte teksten. Den andre og tredje parameteren er størrelsen på tegnene og lengden på strengen. Begge er definert som størrelse_t som er heltall uten fortegn. Resultatet av denne samtalen er å skrive telleelementer av den angitte størrelsen. Merk at med binære filer, selv om du skriver en streng (char *), legger den ikke til noen vognretur eller linjeskifttegn. Hvis du vil ha disse, må du eksplisitt inkludere dem i strengen.

Filmoduser for lesing og skriving av filer

Når du åpner en fil, spesifiserer du hvordan den skal åpnes – om du vil opprette den fra ny eller overskrive den og om den er tekst eller binær, les eller skriv og om du vil legge til den. Dette gjøres ved å bruke en eller flere filmodusspesifikasjoner som er enkeltbokstavene 'r', 'b', 'w', 'a' og '+' i kombinasjon med de andre bokstavene.



  • r - Åpner filen for lesing. Dette mislykkes hvis filen ikke eksisterer eller ikke kan bli funnet.
  • w - Åpner filen som en tom fil for skriving. Hvis filen eksisterer, blir innholdet ødelagt.
  • a - Åpner filen for skriving på slutten av filen (legger til) uten å fjerne EOF-markøren før du skriver nye data til filen; dette oppretter filen først hvis den ikke eksisterer.

Ved å legge til '+' i filmodusen opprettes tre nye moduser:

  • r+ - Åpner filen for både lesing og skriving. (Filen må eksistere.)
  • w+ - Åpner filen som en tom fil for både lesing og skriving. Hvis filen eksisterer, blir innholdet ødelagt.
  • a+ - Åpner filen for lesing og tilføying; tilleggsoperasjonen inkluderer fjerning av EOF-markøren før nye data skrives til filen, og EOF-markøren gjenopprettes etter at skrivingen er fullført. Den oppretter filen først hvis den ikke eksisterer. Åpner filen for lesing og tilføying; tilleggsoperasjonen inkluderer fjerning av EOF-markøren før nye data skrives til filen, og EOF-markøren gjenopprettes etter at skrivingen er fullført. Den oppretter filen først hvis den ikke eksisterer.

Filmoduskombinasjoner

Denne tabellen viser filmoduskombinasjoner for både tekst- og binære filer. Vanligvis leser du enten fra eller skriver til en tekstfil, men ikke begge deler samtidig. Med en binær fil kan du både lese og skrive til samme fil. Tabellen nedenfor viser hva du kan gjøre med hver kombinasjon.

  • r tekst - les
  • rb+ binær - les
  • r+ tekst - les, skriv
  • r+b binær - les, skriv
  • rb+ binær - les, skriv
  • w tekst - skrive, lage, avkorte
  • wb binær - skriv, opprette, avkorte
  • w+ tekst - les, skriv, skap, avkort
  • w+b binær - les, skriv, skap, avkort
  • wb+ binær - les, skriv, lag, avkort
  • en tekst - skriv, skap
  • ab binær - skriv, skap
  • a+ tekst - les, skriv, skap
  • a+b binær - skriv, skap
  • ab+ binær - skriv, skap

Med mindre du bare oppretter en fil (bruk 'wb') eller bare leser en (bruk 'rb'), kan du slippe unna med å bruke 'w+b'.

Noen implementeringer tillater også andre bokstaver. Microsoft tillater for eksempel:

  • t - tekstmodus
  • c - forplikte seg
  • n - ikke-forpliktende
  • S - optimalisering av caching for sekvensiell tilgang
  • R - caching ikke-sekvensiell (tilfeldig tilgang)
  • T - midlertidig
  • D - slett/midlertidig, som dreper filen når den er lukket.

Disse er ikke bærbare, så bruk dem på egen risiko.

Eksempel på fillagring med tilfeldig tilgang

Hovedårsaken til å bruke binære filer er fleksibiliteten som lar deg lese eller skrive hvor som helst i filen. Tekstfiler lar deg bare lese eller skrive sekvensielt. Med utbredelsen av billige eller gratis databaser som f.eks SQLite ogMySQL, reduserer behovet for å bruke tilfeldig tilgang på binære filer. Tilfeldig tilgang til filposter er imidlertid litt gammeldags, men fortsatt nyttig.

Undersøker et eksempel

Anta at eksemplet viser et indeks- og datafilpar som lagrer strenger i en tilfeldig tilgangsfil. Strengene har forskjellige lengder og er indeksert etter posisjon 0, 1 og så videre.

Det er to ugyldige funksjoner: CreateFiles() og ShowRecord(int recnum). CreateFiles bruker en char * buffer på størrelse 1100 for å holde en midlertidig streng som består av formatstrengen msg etterfulgt av n stjerner der n varierer fra 5 til 1004. To FILE * opprettes begge ved å bruke wb filmodus i variablene ftindex og ftdata. Etter opprettelsen brukes disse til å manipulere filene. De to filene er

  • index.dat
  • data.dat

Indeksfilen inneholder 1000 poster av typen indextype; dette er struct indextype, som har de to medlemmene pos (av typen fpos_t) og størrelse. Den første delen av loopen:

fyller ut strengmeldingen slik.

og så videre. Så dette:

fyller ut strukturen med lengden på strengen og punktet i datafilen der strengen skal skrives.

På dette tidspunktet kan både indeksfilstrukturen og datafilstrengen skrives til sine respektive filer. Selv om disse er binære filer, skrives de sekvensielt. I teorien kan du skrive poster til en posisjon utenfor den nåværende slutten av filen, men det er ikke en god teknikk å bruke og sannsynligvis ikke i det hele tatt bærbar.

Den siste delen er å lukke begge filene. Dette sikrer at den siste delen av filen skrives til disken. Under filskriving går mange av skrivingene ikke direkte til disken, men holdes i buffere med fast størrelse. Etter at en skriving fyller bufferen, blir hele innholdet i bufferen skrevet til disk.

En filspylingsfunksjon fremtvinger flushing, og du kan også spesifisere filspylingsstrategier, men de er beregnet på tekstfiler.

ShowRecord-funksjon

For å teste at en hvilken som helst spesifisert post fra datafilen kan hentes, må du vite to ting: hvor den starter i datafilen og hvor stor den er.

Dette er hva indeksfilen gjør. ShowRecord-funksjonen åpner begge filene, søker til det aktuelle punktet (recnum * sizeof(indextype) og henter et antall byte = sizeof(index).

SEEK_SET er en konstant som spesifiserer hvor fseek gjøres fra. Det er to andre konstanter definert for dette.

  • SEEK_CUR - søk i forhold til gjeldende posisjon
  • SEEK_END - søk absolutt fra slutten av filen
  • SEEK_SET - søk absolutt fra starten av filen

Du kan bruke SEEK_CUR for å flytte filpekeren fremover etter sizeof(index).

Etter å ha fått størrelsen og posisjonen til dataene, gjenstår det bare å hente dem.

Her bruker du fsetpos() på grunn av typen index.pos som er fpos_t. En alternativ måte er å bruke ftell i stedet for fgetpos og fsek i stedet for fgetpos. Paret fseek og ftell fungerer med int, mens fgetpos og fsetpos bruker fpos_t.

Etter å ha lest posten inn i minnet, legges et null-tegn til for å gjøre den om til en egen c-streng . Ikke glem det, ellers får du en krasj. Som før kalles fclose på begge filene. Selv om du ikke mister noen data hvis du glemmer fclose (i motsetning til skrivere), vil du ha en minnelekkasje.