Text file workflows: rewrite, censor, and compare
Read files line by line, rewrite them without corrupting the original, and verify copy, rename, and search operations.
By the end, you’ll be able to…
- Read a file incrementally with iteration, readline, and readlines.
- Rewrite a file safely with a temporary sibling and os.replace.
- Censor words and locate keywords with line numbers.
- Copy, compare, rename, and delete files with explicit policies.
01Read a line at a time
Iterating over an open text file yields one line at a time, including its trailing newline. This keeps memory bounded when the file is large. readline returns one line or an empty string at end of file; readlines returns a list and is convenient only when you deliberately want every line in memory.
Strip the newline, not every space: rstrip('\n') preserves indentation and trailing data that may be meaningful in a fixed-width record. If a file uses Windows line endings, opening in text mode translates them, so the stripped result is portable across platforms.
02Rewrite without corrupting the original
Opening with mode 'w' truncates the existing file before the first write. If the process fails halfway through, the old content is already gone. For an important rewrite, write the complete new content to a temporary file in the same directory, close it, then replace the original with os.replace.
Keeping the temporary file beside the target matters: os.replace is atomic only within a filesystem, and a rename across devices may fail. Flush and close before replacing so the new bytes are actually on disk. If validation fails, delete the temporary file and leave the original untouched.
03Censor and mine text
Censoring is a replacement with a policy. Decide whether matching is case-sensitive, whether whole words only should match, and what the replacement looks like. A plain replace('Donkey', '#####') is fast and predictable for exact text; a regular expression with word boundaries is safer when 'Donkey' must not match inside a larger word.
To locate a keyword in a log, keep the line number from enumerate and report every match. Line numbers are one-based at the human boundary even though Python indices are zero-based. Preserve the surrounding line in the report so a reader can judge the context rather than trusting a bare number.
04Copy, compare, rename, and delete deliberately
shutil.copy2 copies content and metadata; pathlib handles ordinary path manipulation. Compare files by reading bytes when the decision is about exact content, or use filecmp.cmp with shallow=False when you want a standard-library comparison. A comparison that ignores metadata answers a different question from one that checks it.
Path.rename changes the name and can move within a filesystem; Path.replace overwrites an existing destination. Deletion is not reversible in ordinary Python code, so require a clear policy and a confirmation boundary before calling unlink. Test these operations against a temporary directory so a mistake cannot touch real data.
The ideas worth keeping.
- Read incrementally and strip only the newline when surrounding spaces are data.
- Rewrite through a temporary sibling, then replace the original atomically.
- Deletion needs an explicit policy; copy and compare need a defined definition of equality.
Make it yours.
Type and test your code right here in the interactive workspace. Run your solution, check the output, and compare with the worked guide when ready.
Censor a list of words safely
Given a file containing text, replace every occurrence of each word in a supplied list with '#####'. Write the result through a temporary sibling and os.replace, then print the final content.
Explore a worked solution
from pathlib import Pathfrom tempfile import TemporaryDirectoryimport oswords = ["Donkey", "donkey"]with TemporaryDirectory() as folder: path = Path(folder) / "chat.txt" path.write_text("Donkey is here\ndonkey again\n", encoding="utf-8") text = path.read_text(encoding="utf-8") for word in words: text = text.replace(word, "#####") temp = path.with_suffix(".tmp") temp.write_text(text, encoding="utf-8") os.replace(temp, path) print(path.read_text(encoding="utf-8"), end="")The loop applies each replacement policy to the in-memory text. The temporary file receives the complete result before the original is replaced, so a failure cannot leave a half-written chat log.
Find the line number of a keyword
Write find_lines(path, keyword) that returns a list of one-based line numbers where keyword appears. Match case-insensitively and demonstrate the result on a small temporary file.
Explore a worked solution
from pathlib import Pathfrom tempfile import TemporaryDirectorydef find_lines(path, keyword): matches = [] with path.open(encoding="utf-8") as handle: for number, line in enumerate(handle, start=1): if keyword.lower() in line.lower(): matches.append(number) return matcheswith TemporaryDirectory() as folder: path = Path(folder) / "log.txt" path.write_text("start\nPython ready\nstop\npython again\n", encoding="utf-8") print(find_lines(path, "python"))The function returns [2, 4]. Keeping the search in a function makes it testable with temporary files, and enumerate supplies human-readable line numbers without manual counting.
What stayed with you?
Check the ideas while they’re fresh. Your answers stay in this page and feedback appears as you go.
Censor a list of words safely
Given a file containing text, replace every occurrence of each word in a supplied list with '#####'. Write the result through a temporary sibling and os.replace, then print the final content.
path.read_text(encoding="utf-8") end="" path.read_text(encoding="utf-8") end=""
Keep exploring.
The official references behind this lesson, when you want a second angle or the full details.
One lesson closer.
Reading is a start. Trying it yourself makes the connection.