Filoperasjoner i Python

Filoperasjoner i Python

Denne artikkelen forklarer filoperasjoner i Python.

Med «sikkerhet», «effektivitet» og «lesbarhet» i tankene, forklarer denne guiden konseptene trinn for trinn, fra det grunnleggende til praktiske teknikker.

YouTube Video

Filoperasjoner i Python

Filoperasjoner er en essensiell grunnleggende ferdighet, fra små skript til store applikasjoner.

Åpne og lukke filer

Først, la oss se på eksempler på lesing og skriving av tekstfiler. Bruk av with-setningen (kontekstbehandler) sørger for at filen lukkes riktig.

Følgende kode åpner en tekstfil, leser innholdet og behandler det linje for linje.

1# Open and read a text file safely using a context manager.
2with open("example.txt", "r", encoding="utf-8") as f:
3    # Read all lines lazily (as an iterator) and process each line.
4    for line in f:
5        print(line.rstrip())
  • Å angi encoding="utf-8" eksplisitt bidrar til å redusere plattformavhengige problemer.
  • Selv med store filer er for line in f minneeffektiv.

Skriving (overskriving og tillegg)

Vær oppmerksom på modusen når du skriver til filer. w er for overskriving, a er for å legge til. Bruk også with-setningen når du skriver.

Følgende kode viser grunnleggende eksempler på overskriving og tillegg.

1# Write (overwrite) to a file.
2with open("output.txt", "w", encoding="utf-8") as f:
3    f.write("First line\n")
4    f.write("Second line\n")
5
6# Append to the same file.
7with open("output.txt", "a", encoding="utf-8") as f:
8    f.write("Appended line\n")
  • Denne koden skriver tekst til output.txt og legger deretter til mer tekst i samme fil. "w"-modusen overskriver filen, mens "a"-modusen legger til en ny linje på slutten av det eksisterende innholdet.
  • Hvis du trenger å flushe, kall flush(), men vanligvis blir innholdet flushet automatisk når konteksten avsluttes.
  • Hvis flere prosesser eller tråder kan skrive samtidig, må du vurdere eksklusiv kontroll, slik som fildåsing.

Lese og skrive binære data

Bilder og komprimerte filer håndteres i binær modus (rb eller wb). I motsetning til tekstmodus ignoreres encoding.

Følgende kode leser en binær fil og kopierer den til en annen fil i binær modus.

1# Read and write binary files.
2with open("image.png", "rb") as src:
3    data = src.read()
4
5with open("copy.png", "wb") as dst:
6    dst.write(data)
  • Når du håndterer store binærfiler, unngå å lese alt på én gang med read(); å lese og skrive i biter er mer minneeffektivt.

Eksempel på håndtering av store filer i biter

For store filer som ikke får plass i minnet, les og skriv dem i biter med fast størrelse. Siden dette er I/O-begrenset, er det nyttig å justere bufferstørrelsen.

Følgende kode kopierer en fil i biter på 64KB. Dette opererer raskt samtidig som minnebruken holdes lav.

1# Copy a large file in chunks to avoid using too much memory.
2CHUNK_SIZE = 64 * 1024  # 64 KB
3
4with open("large_source.bin", "rb") as src,      open("large_dest.bin", "wb") as dst:
5    while True:
6        chunk = src.read(CHUNK_SIZE)
7        if not chunk:
8            break
9        dst.write(chunk)
  • Du kan justere bitstørrelsen etter egenskapene til nettverket eller disken. På SSD-er fungerer en litt større bitstørrelse ofte bedre.

Eksempel på bruk av buffering-argumentet

Ved å spesifisere buffering-argumentet i open()-funksjonen, kan du kontrollere bufferstørrelsen som brukes internt av Python. Dette lar deg ytterligere optimalisere inn-/utdata-effektiviteten.

 1# Explicitly set the internal buffer size to 128 KB for faster I/O.
 2CHUNK_SIZE = 64 * 1024
 3BUFFER_SIZE = 128 * 1024  # 128 KB internal buffer
 4
 5with open("large_source.bin", "rb", buffering=BUFFER_SIZE) as src,      open("large_dest.bin", "wb", buffering=BUFFER_SIZE) as dst:
 6    while True:
 7        chunk = src.read(CHUNK_SIZE)
 8        if not chunk:
 9            break
10        dst.write(chunk)
  • Hvis du setter verdien av buffering til 0, utføres I/U uten buffering; 1 aktiverer linjebuffering, mens verdier på 2 eller høyere bruker en buffer av den spesifiserte størrelsen i byte.
  • Vanligvis er standardverdien tilstrekkelig fordi operativsystemet håndterer buffering effektivt, men å justere denne parameteren kan være effektivt for svært store filer eller spesielle enheter.

Moderne filoperasjoner ved bruk av Pathlib

Standardmodulen pathlib gjør håndtering av stier mer intuitivt. Det forbedrer lesbarhet og sikkerhet sammenlignet med strengbaserte filstier.

 1from pathlib import Path
 2
 3path = Path("data") / "notes.txt"
 4
 5# Ensure parent directory exists.
 6path.parent.mkdir(parents=True, exist_ok=True)
 7
 8# Write and read using pathlib.
 9path.write_text("Example content\n", encoding="utf-8")
10content = path.read_text(encoding="utf-8")
11print(content)
  • Denne koden demonstrerer hvordan du oppretter en katalog med pathlib, skriver til en tekstfil og leser innholdet. Med et Path-objekt kan du håndtere stier både intuitivt og trygt.
  • Path har praktiske API-er som iterdir() og glob(). Du kan skrive kode uten å bekymre deg for filskilletegn mellom ulike operativsystemer.

Midlertidige filer og kataloger (tempfile)

Midlertidige filer kan opprettes trygt med tempfile. Dette unngår sikkerhetsrelaterte feiltilstander og navnekollisjoner.

Følgende kode viser et eksempel på å opprette midlertidige data ved bruk av midlertidig fil.

 1import tempfile
 2
 3# Create a temporary file that is deleted on close.
 4with tempfile.NamedTemporaryFile(
 5    mode="w+",
 6    encoding="utf-8",
 7    delete=True
 8) as tmp:
 9    tmp.write("temporary data\n")
10    tmp.seek(0)
11    print(tmp.read())
12
13# tmp is deleted here
  • Denne koden oppretter en midlertidig fil, skriver og leser data, og sletter den automatisk når with-blokken avsluttes. Ved å bruke tempfile.NamedTemporaryFile kan du håndtere midlertidige filer sikkert og konfliktfritt. Fordi delete=True er spesifisert, slettes filen automatisk.
  • På Windows kan du kanskje ikke åpne filen fra et annet håndtak umiddelbart, så du kan sette delete=False og håndtere slettingen selv.

shutil: Høynivåoperasjoner for kopiering, flytting og sletting

Rekursiv kopiering, flytting og sletting av filer og kataloger er enkelt med shutil.

 1import shutil
 2
 3# Copy a file preserving metadata.
 4shutil.copy2("source.txt", "dest.txt")
 5
 6# Move a file or directory.
 7shutil.move("old_location", "new_location")
 8
 9# Remove an entire directory tree (use with caution).
10shutil.rmtree("some_directory")
  • shutil.copy2 kopierer også metadata (som endringstidspunkt). move vil falle tilbake på å flytte filen selv om «rename» ikke kan benyttes.
  • rmtree er en farlig operasjon, så bekreft og ta alltid sikkerhetskopi før du sletter.

Filmetadata (os.stat) og håndtering av tillatelser

Filstørrelse, endringstidspunkt og tillatelser kan leses og endres med os og stat.

Følgende kode henter grunnleggende filinformasjon med os.stat og endrer tillatelser med os.chmod.

 1import os
 2import stat
 3from datetime import datetime
 4
 5st = os.stat("example.txt")
 6print("size:", st.st_size)
 7print("modified:", datetime.fromtimestamp(st.st_mtime))
 8
 9# Make file read-only for owner.
10os.chmod("example.txt", stat.S_IREAD)
  • Tillatelser oppfører seg forskjellig på POSIX- og Windows-systemer. Hvis plattformuavhengighet er viktig, bruk høynivå-API-er eller legg til betinget håndtering.

Fildåsing (eksklusiv kontroll) — Forskjeller mellom Unix og Windows

Eksklusiv kontroll er nødvendig når flere prosesser har tilgang til samme fil parallelt. UNIX bruker fcntl, og Windows bruker msvcrt.

Følgende kode bruker fcntl.flock på UNIX-systemer for å oppnå en eksklusiv lås ved skriving.

 1# Unix-style file locking example
 2import fcntl
 3
 4with open("output.txt", "a+", encoding="utf-8") as f:
 5    fcntl.flock(f, fcntl.LOCK_EX)
 6    try:
 7        f.write("Safe write\n")
 8        f.flush()
 9    finally:
10        fcntl.flock(f, fcntl.LOCK_UN)
  • Denne koden oppnår en eksklusiv lås med fcntl.flock på UNIX-lignende systemer for å skrive trygt og forhindre samtidige skrivinger til filen. Frigi alltid låsen etter at behandlingen er ferdig slik at andre prosesser får tilgang.
  • På Windows, bruk msvcrt.locking(). For høynivåbruk, vurder eksterne biblioteker som portalocker eller filelock.

Atomiske filskrive-mønstre

For å unngå filkorrupsjon under oppdateringer, skriv til en midlertidig fil og erstatt den med os.replace ved suksess.

Ved å skrive til en midlertidig fil og deretter erstatte, unngår du korrupsjon dersom et krasj oppstår.

 1import os
 2from pathlib import Path
 3import tempfile
 4
 5def atomic_write(path: Path, data: str, encoding="utf-8"):
 6    # Write to a temp file in the same directory and atomically replace.
 7    dirpath = path.parent
 8    with tempfile.NamedTemporaryFile(
 9        mode="w",
10        encoding=encoding,
11        dir=str(dirpath),
12        delete=False
13    ) as tmp:
14        tmp.write(data)
15        tmp.flush()
16        tempname = tmp.name
17
18    # Atomic replacement (on most OSes)
19    os.replace(tempname, str(path))
20
21# Usage
22atomic_write(Path("config.json"), '{"key":"value"}\n')

os.replace utfører en atomisk erstatning innenfor samme filsystem. Vær oppmerksom på at atomisitet ikke garanteres på tvers av ulike monteringspunkter.

Rask tilgang med mmap (for storskala data)

For tilfeldig tilgang til store filer forbedrer mmap I/O-ytelsen. Dette involverer hovedsakelig binære operasjoner.

Følgende kode minnekartlegger en fil og leser eller skriver spesifikke byteområder. Vær forsiktig når du endrer filstørrelsen.

1import mmap
2
3with open("data.bin", "r+b") as f:
4    mm = mmap.mmap(f.fileno(), 0)  # map entire file
5    try:
6        print(mm[:20])  # first 20 bytes
7        mm[0:4] = b"\x00\x01\x02\x03"  # modify bytes
8    finally:
9        mm.close()
  • Denne koden kartlegger en binærfil til minnet med mmap og utfører direkte lese-/skriveoperasjoner på bytenivå. Minnekartlegging muliggjør rask tilfeldig tilgang til store datasett.
  • mmap er effektivt, men feil bruk kan føre til datakonsistensproblemer. Kall flush() for å synkronisere ved behov.

CSV / JSON / Pickle: Lesing og skriving etter format

Spesifikke dataformater har dedikerte moduler. Bruk csv for CSV, json for JSON og pickle for å lagre Python-objekter.

Følgende kode gir grunnleggende eksempler på lesing og skriving av CSV og JSON, samt bruk av Pickle. Fordi Pickle kan kjøre vilkårlig kode, bør du unngå å laste data fra upålitelige kilder.

 1import csv
 2import json
 3import pickle
 4
 5# CSV write
 6with open("rows.csv", "w", encoding="utf-8", newline="") as f:
 7    writer = csv.writer(f)
 8    writer.writerow(["name", "age"])
 9    writer.writerow(["Alice", 30])
10
11# JSON write
12data = {"items": [1, 2, 3]}
13with open("data.json", "w", encoding="utf-8") as f:
14    json.dump(data, f, ensure_ascii=False, indent=2)
15
16# Pickle write (only for trusted environments)
17obj = {"key": "value"}
18with open("obj.pkl", "wb") as f:
19    pickle.dump(obj, f)
  • Det anbefales å angi newline="" for CSV for å unngå ekstra blanke linjer på Windows. Med ensure_ascii=False beholdes UTF-8-tegn lesbare i JSON.

Direkte lesing og skriving av komprimerte filer (gzip / bz2 / zipfile)

Direkte håndtering av gzip og zip kan spare diskplass. Standardbiblioteket inneholder tilsvarende moduler.

Følgende kode er et enkelt eksempel på lesing og skriving av gzip-filer som tekst.

1import gzip
2
3# Write gzipped text.
4with gzip.open("text.gz", "wt", encoding="utf-8") as f:
5    f.write("Compressed content\n")
6
7# Read gzipped text.
8with gzip.open("text.gz", "rt", encoding="utf-8") as f:
9    print(f.read())
  • Det er en avveining mellom komprimeringsforhold og hastighet avhengig av komprimeringsnivå og format.

Sikkerhets- og sårbarhetstiltak

Følgende punkter kan vurderes for sikkerhets- og sårbarhetstiltak.

  • Ikke bruk upålitelig inndata direkte i filnavn eller stier.
  • Bruk Pickle kun med pålitelige kilder.
  • Minimer kjøreprivilegier, og tildel kun de nødvendige tillatelsene til prosesser som håndterer filer.
  • Bruk midlertidige filer med tempfile, og ikke lagre vanlige filer i offentlige kataloger.

Hvis du bruker brukerinput i filbaner, kreves normalisering og validering. For eksempel, bruk Path.resolve() og sjekk overordnede kataloger.

 1from pathlib import Path
 2
 3def safe_path(base_dir: Path, user_path: str) -> Path:
 4    candidate = (base_dir / user_path).resolve()
 5    if base_dir.resolve() not in candidate.parents and base_dir.resolve() != candidate:
 6        raise ValueError("Invalid path")
 7    return candidate
 8
 9# Usage
10base = Path("/srv/app/data")
11
12# Raises an exception: attempted path traversal outside `base`
13safe = safe_path(base, '../etc/passwd')
  • Vær spesielt forsiktig når du bruker ekstern input som filbaner i webapplikasjoner eller offentlige API-er.

Oppsummering av vanlige mønstre

  • Bruk alltid with-setningen (automatisk lukking).
  • Angi encoding eksplisitt for tekstdata.
  • Les og skriv store filer i biter.
  • Innfør fillåsing for delte ressurser.
  • For kritiske oppdateringer, bruk det atomiske mønsteret 'skriv til en midlertidig fil → os.replace'.
  • Bekreft alltid og lag sikkerhetskopier før du utfører farlige operasjoner (som sletting eller overskriving).
  • Normaliser og valider når du bruker ekstern input som filstier.

Sammendrag

For filoperasjoner er det viktig å bruke trygge og pålitelige teknikker som å bruke with-setningen, eksplisitt spesifisere koding og atomiske skrivinger. For storskala prosessering eller parallell tilgang er det nødvendig å implementere låse- og loggstyringssystemer for å forhindre datakorrupsjon og konflikter. Å balansere effektivitet og sikkerhet er nøkkelen til pålitelige filoperasjoner.

Du kan følge med på artikkelen ovenfor ved å bruke Visual Studio Code på vår YouTube-kanal. Vennligst sjekk ut YouTube-kanalen.

YouTube Video