Filoperationer i Python

Filoperationer i Python

Den här artikeln förklarar filoperationer i Python.

Med "säkerhet", "effektivitet" och "läsbarhet" i åtanke förklarar denna guide steg för steg, från grunderna till praktiska tekniker.

YouTube Video

Filoperationer i Python

Filhantering är en grundläggande färdighet, från små skript till storskaliga applikationer.

Öppna och stänga filer

Låt oss först titta på exempel på att läsa och skriva textfiler. Genom att använda with-satsen (kontexthanterare) säkerställs att filen stängs korrekt.

Följande kod öppnar en textfil, läser dess innehåll och bearbetar det rad för rad.

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())
  • Genom att explicit ange encoding="utf-8" minskar plattformsberoende problem.
  • Även med stora filer är for line in f minnesvänligt.

Skriva (skriva över och lägga till)

Var uppmärksam på läget när du skriver till filer. w används för att skriva över, a används för att lägga till. Använd också with-satsen när du skriver till filer.

Följande kod visar grundläggande exempel på att skriva över och lägga till.

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")
  • Den här koden skriver text till output.txt och lägger sedan till mer text i samma fil. Läget "w" skriver över filen, medan "a" lägger till en ny rad i slutet av det befintliga innehållet.
  • Om du behöver spola (flush), anropa flush(), men vanligtvis spolas innehållet automatiskt när kontexten avslutas.
  • Om flera processer eller trådar kan skriva samtidigt måste du överväga exklusiv kontroll, såsom fillåsning.

Läsa och skriva binär data

Bilder och komprimerade filer hanteras i binärt läge (rb eller wb). Till skillnad från textläge ignoreras encoding i binärt läge.

Följande kod läser en binär fil och kopierar den till en annan fil i binärt läge.

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 hanterar stora binärfiler, undvik att läsa allt på en gång med read(); att läsa och skriva i segment är mer minnesvänligt.

Exempel på hantering av stora filer i segment

För stora filer som inte får plats i minnet, läs och skriv dem i segment med fast storlek. Eftersom det är I/O-beroende är det användbart att justera buffertstorleken.

Följande kod kopierar en fil i 64KB-stora segment. Detta arbetar snabbt samtidigt som minnesanvändningen hålls låg.

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 justera segmentstorleken beroende på egenskaperna hos ditt nätverk eller din disk. På SSD-enheter fungerar ofta en något större segmentstorlek bättre.

Exempel på att använda argumentet buffering

Genom att ange argumentet buffering i funktionen open() kan du kontrollera buffertstorleken som används internt av Python. Detta gör att du kan optimera in- och utmatningseffektiviteten ytterligare.

 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)
  • Om du sätter värdet på buffering till 0 utförs I/O utan buffring; 1 möjliggör radbuffring, medan värden på 2 eller högre använder en buffert av angiven storlek i byte.
  • Generellt sett är standardvärdet tillräckligt eftersom operativsystemet effektivt hanterar cachning, men att justera denna parameter kan vara effektivt för mycket stora filer eller speciella enheter.

Moderna filoperationer med Pathlib

Standardmodulen pathlib gör hanteringen av sökvägar mer intuitiv. Det förbättrar läsbarheten och säkerheten jämfört med sökvägar som strängar.

 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)
  • Den här koden visar hur du skapar en katalog med pathlib, skriver till en textfil och läser dess innehåll. Med ett Path-objekt kan du hantera sökvägar både intuitivt och säkert.
  • Path har smidiga API:er som iterdir() och glob(). Du kan skriva kod utan att oroa dig för sökvägsavgränsare mellan olika operativsystem.

Temporära filer och kataloger (tempfile)

Temporära filer kan skapas säkert med tempfile. Detta undviker race conditions samt namnkonflikter.

Följande kod visar ett exempel på hur man skapar temporär data med hjälp av en temporär 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
  • Denna kod skapar en temporär fil, skriver och läser data och tar automatiskt bort filen när with-blocket avslutas. Med tempfile.NamedTemporaryFile kan du hantera temporära filer säkert och utan konflikter. Eftersom delete=True är angivet tas filen bort automatiskt.
  • På Windows kanske du inte kan öppna filen från ett annat handtag direkt, så du kan ange delete=False och själv hantera borttagningen.

shutil: Hög nivå-operationer för att kopiera, flytta och ta bort

Rekursiv kopiering, flyttning och radering av filer och kataloger är 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 kopierar även metadata (som ändringstid). move försöker flytta filen även om byta namn inte kan användas.
  • rmtree är en farlig åtgärd, så bekräfta och säkerhetskopiera alltid före radering.

Filmetadata (os.stat) och hantering av behörigheter

Filstorlek, ändringstid och behörigheter kan läsas och ändras med os och stat.

Följande kod hämtar grundläggande filinformation med os.stat och ändrar behörigheter 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)
  • Behörigheter fungerar olika på POSIX- och Windows-system. Om plattformsoberoende är viktigt, använd hög nivå-API:er eller lägg till villkorlig hantering.

Fillåsning (exklusiv kontroll) — Skillnader mellan Unix och Windows

Exklusiv kontroll krävs när flera processer parallellt får åtkomst till samma fil. UNIX använder fcntl och Windows använder msvcrt.

Följande kod använder fcntl.flock på UNIX-system för att erhålla ett exklusivt lås vid skrivning.

 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)
  • Denna kod får ett exklusivt lås med fcntl.flock på UNIX-liknande system för att skriva säkert och förhindra samtidiga skrivningar till filen. Släpp alltid låset efter bearbetning för att tillåta åtkomst av andra processer.
  • På Windows, använd msvcrt.locking(). För mer avancerad användning, överväg externa bibliotek som portalocker eller filelock.

Atomära filskrivningsmönster

För att undvika filskador vid uppdateringar, skriv till en temporär fil och ersätt den med os.replace vid framgång.

Genom att skriva till en temporär fil och sedan ersätta den undviks filskador om ett avbrott inträffar.

 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ör en atomär ersättning inom samma filsystem. Observera att atomicitet inte garanteras över olika monterade filsystem.

Snabb åtkomst med mmap (för storskalig data)

För slumpmässig åtkomst till stora filer förbättrar mmap I/O-prestandan. Det handlar främst om binära operationer.

Följande kod minnesmappar en fil och läser eller skriver specifika byteintervall. Var försiktig vid ändring av filstorleken.

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()
  • Den här koden mappar en binär fil till minnet med mmap och utför direkta byte-nivå läs-/skrivoperationer. Minnesmappning möjliggör snabb slumpmässig åtkomst till stora datamängder.
  • mmap är effektivt, men felanvändning kan leda till datakonsistensproblem. Anropa flush() för att synkronisera vid behov.

CSV / JSON / Pickle: Läsning och skrivning per format

Specifika dataformat har egna moduler. Använd csv för CSV, json för JSON och pickle för att spara Python-objekt.

Följande kod visar grundläggande exempel på att läsa och skriva CSV och JSON samt använda Pickle. Eftersom Pickle kan köra godtycklig kod, undvik att läsa in data från opålitliga källor.

 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 rekommenderas att ange newline="" för CSV för att undvika extra tomma rader på Windows. Med ensure_ascii=False förblir UTF-8-tecken i JSON läsbara.

Direkt läsning och skrivning av komprimerade filer (gzip / bz2 / zipfile)

Direkt hantering av gzip och zip kan spara diskutrymme. Standardbiblioteket innehåller motsvarande moduler.

Följande kod är ett enkelt exempel på att läsa och skriva gzip-filer som text.

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 finns en avvägning mellan komprimeringsgrad och hastighet beroende på komprimeringsnivå och format.

Säkerhets- och sårbarhetsåtgärder

Följande punkter kan beaktas för säkerhets- och sårbarhetsåtgärder.

  • Använd inte opålitlig indata direkt i filnamn eller sökvägar.
  • Använd Pickle endast med pålitliga källor.
  • Minimera körningsbehörigheter och ge endast minsta möjliga rättigheter till processer som hanterar filer.
  • Använd temporära filer med tempfile och lagra inte vanliga filer i publika kataloger.

Om du använder användarindata i filsökvägar krävs normalisering och validering. Använd till exempel Path.resolve() och kontrollera överordnade 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')
  • Var särskilt försiktig om du använder extern indata som filsökvägar i webbappar eller publika API:er.

Sammanfattning av vanliga mönster

  • Använd alltid with-satsen (automatisk stängning).
  • Specificera alltid encoding för textdata.
  • Läs och skriv stora filer i segment.
  • Inför filslåsning för delade resurser.
  • För kritiska uppdateringar, använd det atomära mönstret 'skriv till temporär fil → os.replace'.
  • Bekräfta alltid och skapa säkerhetskopior innan farliga åtgärder utförs (såsom radering eller överskrivning).
  • Normalisera och validera när extern inmatning används som filsökväg.

Sammanfattning

För filoperationer är det viktigt att använda säkra och pålitliga metoder som with-satser, explicit kodningsangivelse och atomära skrivningar. För storskalig bearbetning eller parallell åtkomst är det nödvändigt att implementera låsning och logghanteringssystem för att förhindra datakorruption och konflikter. Att balansera effektivitet och säkerhet är nyckeln till tillförlitliga filoperationer.

Du kan följa med i artikeln ovan med hjälp av Visual Studio Code på vår YouTube-kanal. Vänligen kolla även in YouTube-kanalen.

YouTube Video