Soy un fanático de los archivos de texto simple. Puede que pasen los años y que liberen nuevos formatos y editores potentes, pero nunca sustituirán al viejo y fiel .txt en mi corazón. Hay un sentimiento que no puedo describir cuando veo la interfaz austera, la fuente monoespaciada, y las líneas de texto ajustadas sobre un fondo blanco. No hay nada superfluo que te distraiga. Escribir así te obliga a estar centrado y a componer una imagen o mapa mental de lo que quieres comunicar. Desde el veterano Bloc de notas de Windows al gedit de Linux y sus derivados, he pasado miles de horas con la vista clavada en editores similares.

Y sí, el texto TIENE que estar ajustado a 80 columnas. No puedo aceptarlo si no. ¡Me da igual que ya estemos en 2025! Solía ajustarlo yo mismo, hasta que descubrí una pequeña utilidad de *nix llamada fold. Este comando lo hará por ti, pero hay un problema: no funciona correctamente con caracteres especiales, como acentos. La herramienta se pensará erróneamente que se trata de dos caracteres distintos, y el conteo de columnas saldrá incorrecto. Nada sorprendente, pues es un programa muy antiguo. Así que, ¿cómo arreglarlo?

Bueno, ¿pues no resulta que yo mismo escribí un sustituto en Python que sí soporta esos caracteres? pfold (proper fold, "fold como es debido") funciona del mismo modo que el original en el resto de casos.

Descarga

Código

#!/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()