Cvičení č. 19 rozšířené o poznámky ze cvičení a řešení některých příkladů (ZS 2024/25)¶
- Otevření souboru pomocí
open()
. - Čtení dat pomocí
read()
,readlines()
. - Zápis do souboru pomocí
write()
neboprint()
. - Uzavření souboru pomocí
close()
.
Práce s adresářovou a souborovou strukturou¶
Práce s adresáři v Pythonu může být komplikovaná a bývá nutné použít více importovaných balíčků. Mimojiné z tohoto důvodu byl vytvořen balíček pathlib
, který práci dost zjednodušuje a umožňuje lehce psát multiplatformní skripty pro různé operační systémy. Systémy Windows používají pro zápis cesty k souboru odlišnou syntaxi oproti systémům Linux a MacOS.
Příklad cesty ve Windows: C:\Users\billgates\Documents\chemtrails.txt
Příklad cesty v Linuxu: /home/linustorvalds/linuxftw.txt
Příklad cesty v MacOS: /Users/stevejobs/howtowearasweater.txt
Pro použítí pathlib
je nutné tento modul importovat pomocí import pathlib
. Případně si můžeme vybrat, které součásti modulu chceme použít. Například from pathlib import Path
importuje pouze součást pro práci s cestami.
Modul pathlib
je objektově orientovaný, což přispívá k jednoduchému použití.
Path
a dostupné metody¶
Path
má přímo dostupné základní metody pro zjištění aktuální pracovní složky a také domovského adresáře. Podle použitého operačního systému bude návratová hodnota buď objekt WindowsPath
nebo PosixPath
. Implementace uvnitř se liší, ale navenek (pro naše použití) jsou identické.
Path.cwd()
vrátí objekt s aktuální pracovní složkou.Path.home()
vrátí objekt s cestou do domovské složky.
from pathlib import Path
path = Path.cwd()
home = Path.home()
print("CWD:", path)
print("HOME:", home)
print(type(path), type(home))
# ukázka i ve windows
CWD: /home/user/zpro-2024-public.git HOME: /home/user <class 'pathlib.PosixPath'> <class 'pathlib.PosixPath'>
Objekt Path
lze vytvořit také z libovolné cesty uložené ve stringu. Zde je dobré použít raw string, ve kterém se neprovádí escapování, takže můžeme v klidu psát i zpětná lomítka jako normální znak.
path = Path(r"C:\Windows")
(Windows)path = Path("/users/hatsunemiku")
(Linux/MacOs)
path = Path(r"C:\Windows")
print(path)
if path.exists():
print(" existuje")
else:
print(" neexistuje")
path = Path("./data/example-files")
print(path)
if path.exists():
print(" existuje")
else:
print(" neexistuje")
# ukázka i ve windows
C:\Windows neexistuje data/example-files existuje
pathlib
také dovoluje spojování cest do jedné. K tomu může například posloužit operátor /
, který známe jako dělení.
home = Path.home()
download = Path("Download")
file = Path("rickroll.mp4")
full_path = home / download / file
print(full_path)
full_path = home / "Download" / "pom.txt"
print(full_path)
/home/user/Download/rickroll.mp4 /home/user/Download/pom.txt
Pokud se vám tento zápis nelíbí, lze též použít metodu joinpath()
, která bere jako parametry stringy nebo objekty Path
.
full_path2 = Path.home().joinpath("Download", Path("rickroll.mp4"))
print(full_path2)
full_path2 = home.joinpath("Download", "rickroll.mp4")
print(full_path2)
full_path2 = home.joinpath(download, file)
print(full_path2)
/home/user/Download/rickroll.mp4 /home/user/Download/rickroll.mp4 /home/user/Download/rickroll.mp4
Součásti cesty¶
Cestu ke složce nebo souboru lze v operačních systémech rozdělit na několik částí, např. přípona souboru, jméno souboru, cesta k němu, atd. pathlib
získávání těchto částí zjednodušuje.
.name
: Vrací jméno souboru bez cesty..stem
: Vrací jméno souboru bez přípony..suffix
: Vrací příponu.anchor
: Část cesty před složkami..parent
: Vrací složku, ve které je soubor, nebo nadřazenou složku dané složce.
from pathlib import Path
path = Path.home() / "recept_na_rumove_pralinky.html"
print("Celá cesta:", path)
print("Name:", path.name)
print("Stem:", path.stem)
print("Suffix:", path.suffix)
print("Anchor:", path.anchor)
print("Parent:", path.parent)
Celá cesta: /home/user/recept_na_rumove_pralinky.html Name: recept_na_rumove_pralinky.html Stem: recept_na_rumove_pralinky Suffix: .html Anchor: / Parent: /home/user
Přejmenování a kopírování souborů¶
Pro nahrazení jména souboru nebo jeho přípony něčím novým lze využít následující metody objektu Path
:
.with_suffix("nová přípona")
v cestě k souboru nahradí starou příponu za novou..with_name("nové jméno souboru včetně přípony")
v cestě k souboru nahradí celé jméno souboru za nové..with_stem("nové jméno souboru")
nahradí v cestě původní jméno za nové, příponu zanechá.
Následně je třeba použít metodu .replace()
, aby se změny provedly na disku. Více v následujícím příkladu.
from pathlib import Path
original_path = Path("data/rename.txt")
# změna přípony
html_path = original_path.with_suffix(".html")
original_path.replace(html_path)
print(f"Renamed {original_path} to {html_path}")
Renamed data/rename.txt to data/rename.html
# změna jména
new_stem_path = html_path.with_stem("newname")
html_path.replace(new_stem_path)
print(f"Renamed {html_path} to {new_stem_path}")
Renamed data/rename.html to data/newname.html
# změna celého jména včetně přípony
new_name_path = new_stem_path.with_name("readme.md")
new_stem_path.replace(new_name_path)
print(f"Renamed {new_stem_path} to {new_name_path}")
Renamed data/newname.html to data/readme.md
Při přejmenovávání souborů je případný existující soubor automaticky přepsán. Je třeba na to dávat pozor. Lze si ale pomoci detekcí, zda soubor s novým jménem existuje, pomocí metody .exists()
, která vrací True
nebo False
.
from pathlib import Path
src = Path("data/readme.md")
dest = src.with_suffix(".exe")
if not dest.exists():
src.replace(dest)
Kopírování souborů je trošku komplikovanější, protože pathlib
nemá přímou podporu. Ale lze to vyřešit pomocí metod .read_bytes()
a .write_bytes()
.
from pathlib import Path
src = Path("data/readme.exe")
dest = src.with_name("dontreadme.md")
dest.write_bytes(src.read_bytes())
39
Soubory lze smazat pomocí metody .unlink()
.
Složky lze mazat pomocí metody .rmdir()
.
from pathlib import Path
src = Path("data/dontreadme.md")
if src.exists():
src.unlink()
Vytváření, čtení, zápis souborů a složek¶
Modul pathlib
dovoluje i zjednodušený zápis a čtení souborů. K tomu slouží metody třídy Path
.
.read_text()
otevře soubor v textovém režimu a načte obsah do stringu..read_bytes()
otevře soubor v binárním režimu a načte jeho obsah jako byty..write_text()
otevře soubor v textovém režimu a zapíše do něj text..write_bytes()
otevře soubor v binárním režimu a zapíše binární data..touch()
vytvoří prázdný soubor. Pokud existuje, změní čas modifikace na aktuální čas..mkdir()
vytvoří novou složku. Nelze vytvořit více zanořených podsložek najednou.
Tyto metody rovnou soubor otevírají a zavírají. Není třeba se o to starat.
from pathlib import Path
textfile = Path("data/text.txt")
file_contents = textfile.read_text()
print(file_contents)
Okolo lesa pole lán, hoj jede, jede z lesa pán, na vraném bujném jede koni, vesele podkovičky zvoní, jede sám a sám. A před chalupou s koně hop a na chalupu: klop, klop, klop! „Hola hej! otevřte mi dvéře, zbloudil jsem při lovení zvěře, dejte vody pít!“
from pathlib import Path
data = ["Snorlax", "Bulbasaur", "Mewtwo", "Smoochum"]
new_text_file = Path("data/pokemon.txt")
new_text_file.write_text("\n".join(data))
33
from pathlib import Path
path = Path("data/touch.html")
path.touch()
from pathlib import Path
path = Path("data/subfolder")
path.mkdir()
path = path / "subsubfolder"
path.mkdir()
if path.exists():
path.rmdir()
if path.parent.exists():
path.parent.rmdir()
Obsah složky (adresáře)¶
Pro získání obsahu složky lze použít metodu .glob()
, která podle zadaného filtru vrátí odpovídající položky. Případně lze procházet složku pomocí metody .iterdir()
.
Path
též dovoluje zjišťovat, o jaký typ cesty se jedná. Zda je to soubor, složka, symbolický link nebo další.
.is_dir()
vrátíTrue
, pokud se jedná o složku (adresář)..is_file()
vrátíTrue
, pokud se jedná o soubor..is_symlink()
vrátíTrue
, pokud se jedná o symbolický link.
path = Path("data/testing-folder")
g = path.iterdir()
print(g)
print(next(g))
<generator object Path.iterdir at 0x7f08e8de8520> data/testing-folder/.ipynb_checkpoints
from pathlib import Path
path = Path().cwd() / "data/testing-folder"
for file in path.iterdir():
print(f"{file}\nFolder: {file.is_dir()} File: {file.is_file()}")
/home/user/zpro-2024-public.git/data/testing-folder/.ipynb_checkpoints Folder: True File: False /home/user/zpro-2024-public.git/data/testing-folder/place_for_testing_file_operations Folder: False File: True
from pathlib import Path
path_list = Path("data").glob("*.txt")
for file in path_list:
print(file)
data/pokemon.txt data/new_file.txt data/text.txt data/text_novy.txt data/inventory.txt data/sinus.txt data/n.txt data/data.txt
Příklady¶
- Ve složce
data/testing-folder
vytvořte novou podsložku se jménemtext-files
. V této nové složce vytvořte 20 textových souborů se jménemfile##.txt
, kde##
odpovídá číslům 0 až 19. Do souboru zapište nějaký neprázdný text.
# Path, mkdir, /, write_text
"12".rjust(3,'0')
from pathlib import Path
new_folder_path = Path("data/testing-folder/text-files")
if not new_folder_path.exists():
new_folder_path.mkdir()
for i in range(20):
name = f"file{str(i).rjust(2,'0')}.txt"
new_file_path = new_folder_path / name
new_file_path.write_text(f"nějaký text {i}")
path = new_folder_path
for file_path in path.iterdir():
print(file_path.name, ":", file_path.read_text())
- Soubory
.txt
vytvořené v minulém příkladě přejmenujte tak, aby měly příponu.md
.
# Path, glob nebo iterdir, with_suffix, replace
# glob
from pathlib import Path
for filepath in path.glob("*.txt"):
# with_suffix, replace
newpath = filepath.with_suffix(".md")
filepath.replace(newpath)
for file_path in path.iterdir():
print(file_path.name)
- Smažte po sobě všechny soubory
.md
a také složkutext-files
.
# iterdir nebo glob, unlink, rmdir
if path.exists():
for filepath in path.glob("*.md"):
filepath.unlink()
path.rmdir()
# smažeme i skryté soubory:
def remove_all(path):
if path.is_dir():
for file in path.iterdir():
remove_all(file)
path.rmdir()
else:
path.unlink()
if path.exists():
remove_all(path)
- Spočítejte kolik různých typů souborů existuje ve složce
data/example-files
a u každého typu též spočítejte, kolik takových souborů existuje.
# iterdir, suffix
folder = Path("data/example-files")
d = {}
for file in folder.iterdir():
if not file.suffix in d:
d[file.suffix] = 1
else:
d[file.suffix] += 1
print(d)
- Ukázkové soubory ze složky
data/example-files
roztřiďte podle roku a měsíce uvedeného ve jméně souboru (formát jména jedata-YYYY-MM-DD.EXT
) do složek s číslem roku a měsíce. Roztříděné soubory nakopírujte do složkydata/testing-folder
.
Příklad: Soubor data-2002-01-01.md
nakopírujeme do složky data/testing-folder/2002/01
.
# iterdir, /, exists, mkdir, name, read_bytes, write_bytes
from pathlib import Path
folder = Path("data/example-files")
for file in folder.iterdir():
s = file.name.split("-") # ["data", "2000", "01", "01.md"]
dest_folder = Path(f"data/testing-folder/{s[1]}/{s[2]}") # adresář, kam budu kopírovat
if not dest_folder.parent.exists(): # vytvořím adresář "data/example-files/2000"
dest_folder.parent.mkdir()
if not dest_folder.exists(): # vytvořím adresář "data/example-files/2000/01"
dest_folder.mkdir()
new_file = dest_folder / file.name # nový název souboru včetně cesty
new_file.write_bytes(file.read_bytes()) # překopíruji soubor
The history saving thread hit an unexpected error (OperationalError('attempt to write a readonly database')).History will not be written to the database.
- Vypište celou strukturu složky
data/testing-folder
z minulého příkladu včetně souborů v následujícím formátu (včetně odsazení):
2000
01
data-2000-01-01.html
...
02
data-2000-02-01.txt
...
2001
01
data ...
# iterdir, is_dir, name
Shrnutí kapitoly¶
# opakování:
from pathlib import Path
path0 = Path.cwd()
path_home = Path.home()
path = Path("data/new")
path1 = path / "text.txt"
path0.joinpath(path)
#print(path0, path, path1)
print("Celá cesta:", path)
print("Name:", path.name)
print("Stem:", path.stem)
print("Suffix:", path.suffix)
print("Anchor:", path.anchor)
print("Parent:", path.parent)
# rename
path2 = path1.with_suffix(".html")
path2 = path1.with_name("new.html")
path2 = path1.with_stem("new")
if path1.exists() and not path2.exists():
path1.replace(path2)
#delete
if path2.exists() and path2.is_file():
path2.unlink()
#read-write:
path1.touch()
path1.write_text("ahoj")
text = path1.read_text()
path2.write_bytes(path1.read_bytes()) # copy
#directory
if path2.exists() and path2.is_dir():
path2.rmdir()
if not path2.exists():
path2.mkdir()
gen1 = Path("data").iterdir()
gen2 = Path("data").glob("*")