Last active
August 25, 2025 19:50
-
-
Save DaSalba/1b4f8ab1e026fc00d3e9c1f571050e9c to your computer and use it in GitHub Desktop.
pfold (proper fold) is a small Python script that works like the Linux tool, but properly supports Unicode so lines with special characters will be have the correct length.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| # -*- coding: utf-8 -*- | |
| # "fold" is a well-known Linux tool that wraps a text file at N columns, and | |
| # optionally splits lines at spaces with the -s option. Sadly it does not work | |
| # well with accents and other special characters bigger than one byte, as it | |
| # will incorrectly think that the line is longer than it really is. | |
| # "pfold" (proper fold) strives to work correctly in the aforementioned cases by | |
| # adding Unicode support (UTF-8). It has -w/--width and -s/--spaces switches. | |
| # It will write the result to standard output just like "fold" does. | |
| # Usage: | |
| # pfold.py FILE [-s/--spaces] [-w/--width=WIDTH] | |
| import argparse | |
| from argparse import RawTextHelpFormatter # Show newlines in usage text. | |
| USAGE_TEXT = ( | |
| "Wraps FILE at WIDTH columns (default 80), optionally breaking sentences at spaces.\n" | |
| "The result will be written to standard output. Use redirects (>, >>) to save it.\n" | |
| "Unlike Linux \"fold\", it correctly supports UTF-8 (like accented characters)." | |
| ) | |
| def main(): | |
| argParser = argparse.ArgumentParser(description=USAGE_TEXT, formatter_class=RawTextHelpFormatter) | |
| argParser.add_argument("file", help="the input file to process") | |
| argParser.add_argument("-s", "--spaces", required=False, action='store_true', help="break lines at spaces") | |
| argParser.add_argument("-w", "--width", required=False, dest="width", type=int, help="use WIDTH columns instead of 80", default=80) | |
| args = argParser.parse_args() | |
| try: | |
| # Open the file. | |
| with open(args.file, "r", encoding="utf-8") as inputFile: | |
| # Read it line by line. | |
| for line in inputFile: | |
| # Prevent Windows line endings from being treated as two chars. | |
| line = line.rstrip('\r\n') | |
| # Initialize variables. | |
| currentLine = "" | |
| lastSpacePos = -1 | |
| # Read the line character by character. | |
| for char in line: | |
| # Have we retrieved N characters already? | |
| if len(currentLine) == args.width: | |
| # Do we have to wrap the line at spaces? | |
| if args.spaces: | |
| # Was there a space in this line? | |
| if lastSpacePos == -1: | |
| # If no, print it as it is. | |
| print(currentLine) | |
| # Begin a new line with the N+1 character. | |
| currentLine = char | |
| else: | |
| # Split the line at that last space. | |
| firstPart = currentLine[:lastSpacePos] | |
| lastPart = currentLine[lastSpacePos:].lstrip() | |
| print(firstPart) | |
| # Begin a new line with the rest and N+1. | |
| currentLine = lastPart + char | |
| # Do we wrap anywhere, not just spaces? | |
| else: | |
| print(currentLine) | |
| currentLine = char | |
| lastSpacePos = -1 | |
| # Still less than N characters? | |
| else: | |
| # Add it to the line buffer and increment count. | |
| currentLine += char | |
| # if the character is a space, mark its position. | |
| if char == ' ': | |
| lastSpacePos = len(currentLine) | |
| if currentLine: | |
| print(currentLine) | |
| else: | |
| # Preserve existing newlines. | |
| print() | |
| except FileNotFoundError: | |
| print(f"Error: The file '{args.file}' was not found.") | |
| except Exception as e: | |
| print(f"An error occurred: {e}") | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment