# -*- 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", dest="width", type=int, required=False, 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: # Initialize variables. finalResult = [] currentLine = "" characterCount = 0 lastSpacePos = -1 # Read it line by line. for line in inputFile: # Read the line character by character. for char in line: # Do we have N characters already? if characterCount == args.width: # If character number 81 is a space, or a newline... if char == ' ' or char == '\n': finalResult.append(currentLine) # Reset the line data. currentLine = "" characterCount = 0 lastSpacePos = -1 else: # Do we have to wrap the line at the last space? if args.spaces: # First, we see if there was no space in this line. if lastSpacePos == -1: # In that case, this is a line filled with N symbols (letters, numbers...) and no spaces. finalResult.append(currentLine) # We begin a new line with the 81st character. currentLine = char characterCount = 1 lastSpacePos = -1 else: # We take the first part of the line, from the beginning to the last space. firstPart = currentLine[:lastSpacePos] # And the last part, from there to the end. lastPart = currentLine[lastSpacePos:] # We add the first part to the file, plus a newline. finalResult.append(firstPart) # And begin another line with the last part and the 81st character. currentLine = lastPart + char characterCount = len(currentLine) lastSpacePos = -1 # Do we just wrap here? else: finalResult.append(currentLine) # We begin a new line with the 81st character. currentLine = char characterCount = 1 lastSpacePos = -1 # Still less than N characters? else: # Add to temporal line and increment count. currentLine += char characterCount += 1 # Also, if this character is a space, we mark its position. if char == ' ': lastSpacePos = characterCount # Reached end of line, reset count and add to final file. finalResult.append(currentLine) currentLine = "" characterCount = 0 lastSpacePos = -1 # We print to standard output, skipping the final newline. print("\n".join(finalResult), end="") 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()