What the project will do
A word frequency counter answers a simple question: which words appear most often in a file? This version accepts a file path and an optional number of results to show. It prints one word and count per line, with the most frequent words first. When counts tie, it sorts the words alphabetically so the output is predictable.
You need Python 3.8 or newer and a plain-text file saved as UTF-8. No third-party packages are required. Save the program below as word_count.py. You can run it from a terminal in the same directory as your text file, or pass a path to a file elsewhere. The program reads the whole file into memory, which keeps the example straightforward but makes it better suited to small and moderate files than very large ones.
Decide what counts as a word
Before counting, the program needs a rule for finding words. Here, a word is a sequence of English letters that may contain an internal straight apostrophe, such as don't. The program converts text to lowercase, so Cat and cat share a count. Punctuation around words is ignored, while a hyphen splits a term into two words. Numbers do not count.
That rule is useful for a first project, but it is not a universal definition of a word. In particular, the pattern below matches the letters a through z, not words written in other scripts or letters with accents. A curly apostrophe also behaves differently from a straight one. Stating these choices makes the results easier to understand and gives you clear starting points for later improvements.
Write the program
The standard-library Counter stores how many times each word occurs. argparse handles the file path and --top option; pathlib reads the file. Copy this complete program into word_count.py:
import argparse import re from collections import Counter from pathlib import Path def count_words(path): text = path.read_text(encoding="utf-8") words = re.findall(r"[a-z]+(?:'[a-z]+)?", text.lower()) return Counter(words) def main(): parser = argparse.ArgumentParser( description="Count words in a UTF-8 text file." ) parser.add_argument("file", type=Path, help="Path to a UTF-8 text file") parser.add_argument( "--top", type=int, default=10, help="Number of words to show (default: 10)" ) args = parser.parse_args() if args.top < 1: parser.error("--top must be a positive integer") try: counts = count_words(args.file) except (OSError, UnicodeError) as exc: parser.exit(1, f"Could not read {args.file}: {exc}\n") if not counts: print("No matching words found.") return ranked = sorted(counts.items(), key=lambda item: (-item[1], item[0])) for word, count in ranked[:args.top]: print(f"{word}\t{count}") if __name__ == "__main__": main()
The regular expression extracts matching words, and Counter tallies them. The sort key uses a negative count to put larger counts first, then uses the word to break ties. The program catches file-reading and decoding errors and reports them instead of displaying a traceback. Invalid --top values are rejected before the file is read.
Run it and check the result
Create a UTF-8 file named sample.txt containing exactly this text: Cats chase cats. Dogs chase cats! From a terminal in the directory containing both files, run python3 word_count.py sample.txt --top 3. On a system where Python 3 is invoked with python instead of python3, use that command name. The output should be three lines: cats followed by a tab and 3, chase followed by a tab and 2, and dogs followed by a tab and 1.
Try running the command without --top to see its default of ten results. If the file contains fewer than ten distinct matching words, it prints only those available. Also try a missing filename and --top 0 to see the error messages. If a file cannot be decoded as UTF-8, check its encoding or save a UTF-8 copy before trying again.
Extend the project carefully
Small changes make useful practice. Add a line that prints the total number of matched words with sum(counts.values()), or print the number of distinct words with len(counts). Check an empty file and a file containing only numbers or punctuation; both should produce the no-matching-words message. A file with two equally frequent words lets you verify the alphabetical tie-breaker.
For a larger challenge, decide how your program should treat accented letters, curly apostrophes or hyphenated terms, then update both the matching rule and your test files. There is no single correct choice for every document. What matters is that the program applies its rule consistently and that someone reading its output can tell what was counted.



