Grundlæggende filhåndtering i VB

Tags:    visual-basic
Skrevet af Bruger #460 @ 03.06.2002
Grundlæggende Filhåndtering


Introduktion

Denne artikel omhandler filhåndtering i Microsoft Visual Basic (kaldet VB i denne artikel). Jeg vil gennemgå oprettelse og skrivning til filer såvel som omdøbning, flytning, sletning, kopiering og læsning af filer. Alle eksempler er skrevet i VB version 6.0, men ingen funktioner eller sætninger, der ikke er tilstede i VB version 5.0, bliver brugt. Derfor er eksempelkoden kompatibel med VB5 og opefter. Jeg har ikke checket, om koden er kompatibel med tidligere versioner, men da jeg bruger meget grundlæggende BASIC funktionalitet, skulle koden være kompatibel med endnu ældre versioner end 5.0.

Hvis du ikke har VB-programmeringssproget, kan du sandsynligvis bruge eksempelkoden i andre programmer, der understøtter VBA (Visual Basic for Applications). De fleste versioner af Microsoft Office understøtter i hvert fald VBA. Office-programmerne fra version 97 og opefter understøtter de såkaldte makroer, der i virkeligheden er små stumper VB-kode. I Microsoft Access kaldes disse ofte hændelsesprocedurer mens udtrykket makro bruges til Access-makroer, der er en speciel facilitet i Access.

Jeg antager i denne artikel, at du har administratorrettigheder på det system, du arbejder med. Ydermere antages det, at din harddisk kaldes C: og ikke er skrivebeskyttet eller på anden måde sikret imod filmanipulation. Du kan ikke oprette filer på skrivebeskyttede medier som f.eks. Cd-rom'er og disketter. Du kan dog stadig åbne og læse filer fra disse medier.


Oversigt over filhåndteringsprocessen

Operativsystemet kan tildele enhver fil et tal, der unikt repræsenterer den. Dette tal kaldes et file handle på engelsk eller filnummer på dansk, og kan opnås i VB ved at kalde funktionen; FreeFile. Funktionen bør kaldes umiddelbart inden, en fil åbnes med Open-sætningen. Med Open-sætningen kan du åbne filer i forskellige tilstande. I denne artikel bruges udelukkende binær tilstand , da denne tilstand tillader læsning og skrivning samtidig. Du kan også hoppe frem og tilbage i filen, som du har lyst til. I binær tilstand kan størrelsen (i bytes) på de data, der skrives eller læses, varieres efter behov.

Når du har filen åben, kan du skrive til den med Put sætningen. For at læse fra filen, har du brug for Get sætningen. Begge sætninger skal have at vide hvilken fil (dvs. filnummeret), du vil skrive/læse til/fra. De skal også have en variabel, der henholdsvis sender/modtager data til/fra filen. Antallet af bytes, der skrives/læses, afhænger derfor af variablens størrelse i bytes. Dette kommer vi tilbage til senere.

Det sted i filen, hvor næste læsning eller skrivning kommer til at forgå, kaldes den aktuelle filposition. Du kan oplyse en bestemt filposition i dine Put/Get sætninger, eller du kan bruge den aktuelle, hvad den så end måtte være i øjeblikket. Efterhånden som du skriver og læser fra filen, forandres filpositionen. Det kan sommetider være nødvendigt at finde den aktuelle position, hvilket gøres med funktionen Seek. Denne funktion returnerer et offset fra filens begyndelse til den aktuelle byte. Den første byte i en fil hedder 1, næste byte hedder 2, og så videre. Hvis Seek eksempelvis returnerer 476, vil næste handling altså ske ved byte nummer 476.

Når du er færdig med filen, er det vigtigt at huske at lukke filen. Close sætningen lukker den specificerede fil eller (hvis intet filnummer oplyses) alle åbne filer. Da du kan arbejde med adskillige åbne filer på samme tid, er det smart at lukke filerne enkeltvis, og til sidst i programmet lukkes alle filer for en sikkerheds skyld. Hvis der ikke er nogle åbner filer, når du forsøger at lukke dem, sker der ikke noget. Med andre ord; du kan roligt kalde Close for en sikkerheds skyld, da du ikke får nogen fejl. Det er i øvrigt værd at nævne, at der findes en anden sætning, nemlig Reset i Visual Basic, der ligeledes lukker alle åbne filer.


Oprettelse af en ny fil

Lad os se et eksempel på åbning af en fil. Husk, at hvis den ikke eksisterer allerede, så forsøger VB at oprette den. Vi bruger en streng til filens navn og en Integer til filens nummer.

'
'   Skaf et filnummer, der er unikt for netop denne fil.
'
Dim intFilNummer As Integer
Let intFilNummer = FreeFile

'
'   Brug roden på C: drevet til at oprette filen i. Så 
'   kan vi altid finde den igen.
'
Dim strFilNavn As String
Let strFilNavn = "C:\\MinFil.txt"

'
'   Åbn filen i binær tilstand ved hjælp af det 
'   specificerede filnavn og det fundne filnummer.
'
Open strFilNavn For Binary As #intFilNummer

I eksemplet her lægges filen ud i C: drevets rodbibliotek. Hvis den ikke allerede eksisterer, oprettes den. Hvis du af en eller anden grund allerede har en vigtig fil ved navn minfil.txt i dette bibliotek, bør du bakke den op først eller bruge et andet navn i VB-koden.

For eksemplets skyld antager vi, at filen ikke eksisterede, inden vi åbnede den. Derved er den oprettet, men den indeholder intet endnu. Hvis vi simpelthen lukker den nu med Close, vil den stadig eksistere på harddisken, selvom den indeholder nul bytes. Den er en såkaldt NULL-file eller nulfil på dansk. En nulfil indeholder intet -- ikke engang nul -- men dens navn er oprettet i diskens to FAT-tabeller.

Close-sætningen er meget enkel at bruge; du kan lukke den åbne fil som følger:

'
'   Luk den åbne fil.
'
Close #intFilNummer

Omdøbning af en fil

Vi har nu en fil på disken, vi kan mishandle, som vi vil, mens vi eksperimenterer. Lad os prøve at omdøbe den med Name As sætningen i VB. Name omdøber en eksisterende fil til det navn, du vælger. Her omdøber vi den fra "minfil.txt" til "barnemad.txt":

Name "c:\\minfil.txt" As "c:\\barnemad.txt"

Eller, hvis vi vil bruge variabler i stedet (for at gøre det hele lidt klarere):

'
'   Opret en variabel til det nye filnavn og omdøb så filen.
'
Dim strNytNavn As String
Let strNytName = "barnemad.txt"
Name strFilNavn As strNytNavn   ' strFilNavn = "minfil.txt"

Det nye navn, du bruger til omdøbning, må ikke allerede eksistere på disken. Eksempelvis kan du sandsynligvis ikke omdøbe filen til Autoexec.bat, da der eksisterer en sådan på C:\\ i forvejen. Hvis du kunne, ville det være alt for let at komme til at overskrive filer.


Kopiering, sletning, og flytning af en fil

Det er nemt nok at kopiere en fil fra et sted til et andet i Visual Basic. Kommandoen FileCopy gør dette meget nemt for dig, da den simpelthen vil have at vide, hvilken fil skal kopieres og hvor den skal kopieres hen. Kommandoen forventer to parametre nemlig Source og Destination, dvs. kilde og mål. Hvis du vil kopiere C:\\Windows\\Win.ini til C:\\Backup\\Win.ini, så gøres det således i Visual Basic:

'
'   Opret et par variabler til filnavnene og kopier.
'
Dim strSource   As String
Dim strDest     As String

Let strSource = "c:\\windows\\win.ini"
Let strDest = "c:\\backup\\win.ini"

FileCopy strSource, strDest

Da FileCopy virker på lukkede filer, er det en meget behagelig kommando at bruge. Den virker meget hurtigt, men hvis der opstår en fejl, genererer Visual Basic en kørselsfejl (run-time error), som du så må fange på almindelig vis med On Error Goto.

For at slette en fil fra disken, må filen være lukket. Du kalder siden Kill sætningen, der skal have at vide, hvilken fil, du vil slette. Følgende eksempel sletter den backup-kopi, vi lavede i forrige eksempel:

'
'   Slet backup-kopien af win.ini
'
Dim strFilNavn As String
Let strFilNavn = "c:\\backup\\win.ini"
Kill strFilNavn

Da det at flytte en fil egentlig er det samme som at a) kopiere og siden b) slette, findes der ikke nogen decideret kommando eller sætning, der flytter filer. Det kan gøres med FileCopy og Kill. Lad os antage, at vi gerne vil flytte og omdøbe vores backup fil til Temp mappen i Windows mappe. Den nye kopi af filen skal hedde Win.tmp. Dette kan gøres således:

'
'   Opret et par variabler for at gøre det lettere.
'
Dim strGammelFil    As String   ' den gamle kopi
Dim strNyFil        As String   ' den nye kopi

Let strGammelFil = "c:\\backup\\win.ini"
Let strNyFile = "c:\\windows\\temp\\win.tmp"

FileCopy strGammelFil, strNyFil
Kill strGammelFil

Herved er den gamle fil kopieret og slettet, mens den nye fil har et andet navn end det oprindelige. Det anbefales dog ikke at bruge sådanne indviklede teknikker, hvis du blot vil omdøbe en fil uden at flytte den eller noget. I sådanne tilfælde er det langt mere hensigtsmæssigt at bruge Name As sætningen til formålet, da den ikke opretter unødvendige kopier på disken. Det kan jo være du (eller din bruger) ikke har for meget diskplads til rådighed.


Skrive til og læse fra en fil

Du har fuld kontrol over en fil, når du har åbnet den. Du kan skrive hvad som helst hvor som helst i filen, hvis du har de nødvendige rettigheder og hvis disken er skrivbar. Under Windows 95/98/ME har du altid administratorrettigheder, mens Windows NT-baserede systemer kræver, at du er logget ind som en bruger med de fornødne rettigheder.

Lad os åbne en fil ved navn "minfil.txt" på drev C: og skrive data til den med Put sætningen:

Dim strFilNavn As String
Let strFilNavn = "c:\\minfil.txt"

Dim intFilNummer As Integer
Let intFilNummer = FreeFile

Dim strData As String
Let strData = "Filhåndtering"

Open strFilNavn For Binary As #intFilNummer
Put #intFilNummer, 1, strData
Close #intFilNummer

Efter at koden ovenfor har kørt, skulle der gerne ligge en fil, der hedder "minfil.txt", på roden af drev C:. Denne fils størrelse vil være 13 bytes, da der er 13 tegn i ordet "filhåndtering". Put sætningen i eksemplet har tre felter; filnummer, filposition og data. Filnummeret fortæller VB hvilken fil, der skal arbejdes med. Filpositionen fortæller VB, hvilken position i den åbne fil der skal skrives til. strData variablen fortæller VB, hvor mange bytes og hvilke bytes skal skrives til filen. Efter skrivningen vil den aktuelle filposition være rykket frem til første byte efter de skrevne data.

Du behøver ikke at oplyse en bestemt filposition, når du skriver/læser fra en fil. Hvis du undlader at angive filpositionen, vil VB skrive til den aktuelle position og denne forandres derefter til at pege på den første byte efter de skrevne/læste data. I eksemplet skrives der til position 1 og da strengen er 13 bytes lang, vil filpositionen efter skrivningen være 14. Selvom filen ikke er mere end 13 bytes lang, kan filpositionen altså sættes til 14. Dette vil medføre at yderligere skriveoperationer udvider filen med det antal bytes du skriver.

I følgende eksempel viser jeg, hvordan du kan læse de data, du skrev til filen. Da vi ved, hvor lang strengen er i forvejen, er det ikke noget problem at læse det rigtige antal tegn ind i hukommelsen. I det virkelige liv er historien dog en anden; det vender vi tilbage til om lidt:

Dim strFilNavn As String
Let strFilNavn = "c:\\minfil.txt"

Dim intFilNummer As Integer
Let intFilNummer = FreeFile

Dim strData As String
Let strData = Space$(13)    'indstil længde af datafelt

Open strFilNavn For Binary As #intFilNummer
    Get #intFilNummer, 1, strData
Close #intFilNummer

MsgBox "Læste data ***" & strData & "***"

I dette eksempel bruges Space$ funktionen til at lave en streng med 13 mellemrum. Når du skal læse med Get sætningen, kan det være lige meget, hvad strengen indeholder; det, der tæller, er, hvor mange bytes variablen indeholder. Hvis du f.eks. vil læse hele filen ind i hukommelsen, uanset hvad den indeholder, må du først bestemme filens størrelse. Dette kan gøres med LOF funktionen eller med FileLen funktionen. FileLen har den fordel, at den virker på lukkede filer, mens LOF kun virker på åbne filer. FileLen skal kende filens navn, mens LOF kræver et filnummer. Personligt foretrækker jeg LOF, da den er mere basal og findes i alle BASIC-implementationer, selv gode gamle Microsoft QBasic 1.1 fra MS-DOS. Hvis du udelukkende har intentioner om at bruge din BASIC kode i VB, kan du roligt bruge FileLen, da den er lidt mere fleksibel.

I dette eksempel åbner vi en fil, finder dens længde (LOF står for "Length Of File"), indstiller data bufferen til filens længde og læser dataene:

Dim strFilNavn      As String
Dim intFilNummer    As Integer
Dim strData         As String
Dim lngFilLængde    As Long

Let strFilNavn = "c:\\minfil.txt"
Let intFilNummer = FreeFile
Open strFilNavn For Binary As #intFilNummer

Let lngFilLængde = LOF(intFilNummer)    'find længde
Let strData = Space$(lngFilLængde)      'indstil buffer
Get #intFilNummer, , strData            'læs hele molevitten

Close #intFilNummer

MsgBox "Læste data ***" & strData & "***"

Ovenstående eksempel læser simpelthen hele filen ind i en streng. Hvis du nu havde for eksempel 2 forskellige strenge gemt i filen og ville læse dem begge ind, hvordan kan VB så vide, hvor den ene streng ender, og den næste begynder? Dette er et godt spørgsmål og svaret er: Det kan VB ikke vide!


Håndtering af datatyper med vilkårlig længde

Hvis du bare sender tekst til en fil, f.eks. to strenge, hvor den ene indeholder "filhåndtering", og den anden indeholder "er cool", så vil filen komme til at indeholde "filhåndteringer cool". Derfor bruger de fleste programmører enten en nul-byte (00h) til at indikere slutningen på en streng, eller en eller flere length bytes ("længde-bytes" på dansk) foran strengen til at angive antallet af tegn (bytes) i tekststrengen. Strenge, der afsluttes med 00h, kaldes ofte "ASCIIZ strings", og er meget almindelige i C-verdenen. Personligt synes jeg bedre om at bruge length-bytes, da det gør det meget lettere at læse data ind fra en fil.

Følgende eksempel gemmer to strenge i "minfil.txt" filen, og indsætter to bytes før hver streng for at indikere længden af hver streng.

Dim strFilNavn      As String
Dim intFilNummer    As Integer

Let strFilNavn = "C:\\Minfil.txt"
Let intFilNummer = FreeFile

'
'   Sæt de to strenge op, der skal skrives til filen. 
'   Find også de to længder.
'
Dim strFørsteStreng As String
Dim strAndenStreng  As String
Dim intFørsteLængde As Integer
Dim intAndenLængde  As Integer

Let strFørsteStreng = "Filhåndtering"
Let strAndenStreng = "er cool"
Let intFørsteLængde = Len(strFørsteStreng)
Let intAndenLængde = Len(strAndenStreng)

'
'   Åbn filen i binær tilstand og spyt de to strenge ud 
'   til filen. For hver af de to strenge, skriv længden
'   ud først.
'
Open strFilNavn For Binary As #intFilNummer
    Put #intFilNummer, , intFørsteLængde    ' skriv længde
    Put #intFilNummer, , strFørsteStreng    ' skriv streng
    Put #intFilNummer, , intAndenLængde     ' skriv længde
    Put #intFilNummer, , strAndenLængde     ' skriv streng
Close #intFilNummer

Som sagt, gør de to længde-bytes foran hver streng det nemmere at læse dataene ind fra filen. Her kommer der et eksempel, der læser de to strenge ind fra filen. Ideen er, at VB læser to længde-bytes, indstiller en buffer til at modtage det antal bytes, strengen kræver. Siden gentages processen for den næste streng, der skal indlæses. Til sidst skrives de to strenge ud til debug-vinduet.

Dim intFilNummer As Integer
Let intFilNummer = FreeFile

Dim strFilNavn As String
Let strFilNavn = "c:\\minfil.txt"

Dim strFørsteStreng As String
Dim strAndenStreng  As String
Dim intFørsteLængde As Integer
Dim intAndenLængde  As Integer

Open strFilNavn For Binary As #intFilNummer
'   
'   Indlæs første streng.
'
    Get #intFilNummer, , intFørsteLængde
    Let strFørsteStreng = Space$(intFørsteLængde)
    Get #intFilNummer, , strFørsteStreng
    
    '
    '   Indlæs anden streng.
    '
    Get #intFilNummer, , intAndenLængde
    Let strAndenStreng = Space$(intAndenLængde)
    Get #intFilNummer, , strAndenStreng
Close #intFilNummer

'
'   Udskriv de to strenge.
'
Debug.Print "Første streng..: " & strFørsteStreng
Debug.Print "Anden streng...: " & strAndenStreng

Ved hjælp af de to længde-bytes kan programmet nu læse strengene ind korrekt – hvis altså du har angivet den rette længde på strengen – ellers læser Visual Basic for mange bytes ind i hukommelsen.


Komplekse datatyper

Strenge er ikke den eneste datatype, der kan have en vilkårlig længde. Mens de simple datatyper; Boolean, Byte, Integer, Long, Single, Double, Currency, Date altid har en bestemt længde, kan brugerdefinerede typer også have det. Dette gælder for brugerdefinerede datatyper, der udelukkende består af felter med simple datatyper. Hvis der indgår for eksempel et String felt i en brugerdefineret datatype, har den ikke nødvendigvis en fast længde, da det ene felt har variabel størrelse.

Arrays kan ligeledes bestå af elementer med en fast længde. Hvis dette er tilfældet, kan du med stor fordel skrive antallet af elementer ud til filen, inden du skriver selve arrayet. Derved kan du, når du læser filen ind igen, finde ud af, hvor mange bytes du skal læse ind fra filen for at få hele arrayet ind i hukommelsen. Igen, hvis arrayet består af strenge, begynder det at blive indviklet, da hver enkelt streng kan variere i længde.

VB programmører bruger ofte strenge til at repræsentere binære data. Strenge er meget fleksible i VB, hvilket gør dem rigtig populære. Desværre er de meget langsomme, hvilket gør, at mange programmører vælger at bruge byte-arrays i stedet. Byte arrays er meget hurtigere at arbejde med. Det er ikke umuligt at læse strenge ind i Byte arrays eller Integer arrays, men det kan være besværligt at overføre dem til String variabler, efter at de er læst ind. Dette gør tekst meget besværlig at arbejde med, hvis man ikke har en måde at overføre dem til strenge på. Der vil blive skrevet en artikel i nærmeste fremtid om memory-manipulation, der viser, hvordan denne og mange andre smarte tricks kan udføres med lidt API-programmering.

Variant og Objekt datatyperne har altid samme størrelse, nemlig 16 bytes eller deromkring. De kan dog ikke betragtes som simple datatyper, da de i virkeligheden er pointere til det sted i hukommelsen, hvor data eller objekt ligger opbevaret. Derfor må du aldrig betragte disse som selvstændige variabler. Og du må i hvert fald ikke skrive dem til filer, da du så mister data; i stedet for a gemme det, variablerne peger på, gemmer du adressen, de peger hen på.


Konklusion

Filhåndtering er generelt ikke svær, men der kan blive en masse arbejde ud af det, hvis man ikke kender de forskellige datatyper og forstår, hvordan de virker. Hvis man derimod har check på datatyperne, kan man opbygge enten standard rutiner eller klassemoduler, der kan bruges i mange forskellige programmer i fremtiden.

I fremtiden vil der komme flere artikler fra mig om manipulation af data og filer. Der findes allerede en artikel om opbygning af DLL filer i Visual Basic, og der vil komme en om memory-manipulation snart. Denne artikel kommer til at arbejde med flytning af data fra for eksempel et byte array til en streng og omvendt. Tilsammen vil disse artikler kulminere i et Visual Basic projekt, der håndterer data i filer og i memory, og det hele bliver tilgængeligt i source code.




Hvad synes du om denne artikel? Giv din mening til kende ved at stemme via pilene til venstre og/eller lægge en kommentar herunder.

Del også gerne artiklen med dine Facebook venner:  

Kommentarer (7)

User
Bruger #2353 @ 08.04.03 19:36
Virkelig velskrevet og oplysende artikel.
User
Bruger #460 @ 24.12.03 10:00
Tusind tak for de venlige ord
User
Bruger #6770 @ 08.12.04 17:41
Jeg kan ikke programmet til at virke uden jeg skriver "c:\\minfil.txt" istedet for "c:minfil.txt"
User
Bruger #9836 @ 15.04.06 23:49
hvor kan man downloade VB5 eller 6?
User
Bruger #10770 @ 29.10.06 11:54
Hjælp......... jeg er ved at skrive en kode som skal bruges i inventor men hvordan får jeg kontakt med inventor skal jeg bruge addin
User
Bruger #9692 @ 14.10.07 19:26
Man kan ikke downloadede Visual Basic nogle steder med mindre du vil downloadede det ulovligt og det går jeg ikke ud fra
User
Bruger #13520 @ 10.09.08 21:01
god artikel. Den kommer som kaldet, nu mens jeg har et støre projekt, der kræver config filer, kørende..

stort 5
Du skal være logget ind for at skrive en kommentar.
t