¿Cómo cambiar los bytes en un archivo?

Estoy creando un progtwig de cifrado y necesito abrir el archivo en modo binario para acceder a caracteres no ascii y no imprimibles, necesito verificar si el carácter de un archivo es letra, número, símbolo o carácter no imprimible. Eso significa que tengo que marcar 1 por 1 si los bytes (cuando se decodifican a ascii) coinciden con cualquiera de estos caracteres:

{^9,dzEV=Q4ciT+/s};fnq3BFh% #2!k7>YSU<GyD\I]|OC_e.W0M~ua-jR5lv1wA`@8t*xr'K"[P)&b:g$p(mX6Ho?JNZL 

Creo que podría codificar estos caracteres de arriba a binario y luego compararlos con bytes. No se como hacer esto.

PD Perdón por malentendido inglés y binario. (Espero que sepas a qué me refiero con bytes, me refiero a los caracteres en modo binario como este):

 \x01\x00\x9a\x9c\x18\x00 

Hay dos tipos principales de cadenas en Python: bytestrings (una secuencia de bytes) que representan datos binarios y cadenas de Unicode (una secuencia de puntos de código de Unicode) que representan texto legible por humanos. Es simple convertir uno en otro (☯):

 unicode_text = bytestring.decode(character_encoding) bytestring = unicode_text.encode(character_encoding) 

Si abre un archivo en modo binario, por ejemplo, 'rb' entonces file.read() devuelve un bytestring (tipo de bytes ):

 >>> b'A' == b'\x41' == chr(0b1000001).encode() True 

Hay varios métodos que se pueden usar para clasificar bytes:

  • métodos de cadena como bytes.isdigit() :

     >>> b'1'.isdigit() True 
  • constantes de cadena como string.printable

     >>> import string >>> b'!' in string.printable.encode() True 
  • expresiones regulares como \d

     >>> import re >>> bool(re.match(br'\d+$', b'123')) True 
  • funciones de clasificación en el módulo curses.ascii , por ejemplo, curses.ascii.isprint()

     >>> from curses import ascii >>> bytearray(filter(ascii.isprint, b'123')) bytearray(b'123') 

bytearray es una secuencia mutable de bytes; a diferencia de un bytestring, puede cambiarlo in situ, por ejemplo, a minúsculas cada 3er byte que está en mayúsculas:

 >>> import string >>> a = bytearray(b'ABCDEF_') >>> uppercase = string.ascii_uppercase.encode() >>> a[::3] = [b | 0b0100000 if b in uppercase else b ... for b in a[::3]] >>> a bytearray(b'aBCdEF_') 

Aviso: b'ad' son minúsculas pero b'_' mantuvo igual.


Para modificar un archivo binario in situ, podría usar el módulo mmap , por ejemplo, para minúscula en la cuarta columna en cada otra línea en 'file' :

 #!/usr/bin/env python3 import mmap import string uppercase = string.ascii_uppercase.encode() ncolumn = 3 # select 4th column with open('file', 'r+b') as file, \ mmap.mmap(file.fileno(), 0, access=mmap.ACCESS_WRITE) as mm: while True: mm.readline() # ignore every other line pos = mm.tell() # remember current position if not mm.readline(): # EOF break if mm[pos + ncolumn] in uppercase: mm[pos + ncolumn] |= 0b0100000 # lowercase 

Nota: las API de Python 2 y 3 difieren en este caso. El código utiliza Python 3.

Entrada

 ABCDE1 FGHIJ ABCDE FGHI 

Salida

 ABCDE1 FGHiJ ABCDE FGHi 

Aviso: la cuarta columna se convirtió en minúscula en las líneas 2 y 4 h.


Por lo general, si desea cambiar un archivo: lee el archivo, escribe modificaciones en un archivo temporal y, si lo hace, mueve el archivo temporal al lugar del archivo original:

 #!/usr/bin/env python3 import os import string from tempfile import NamedTemporaryFile caesar_shift = 3 filename = 'file' def caesar_bytes(plaintext, shift, alphabet=string.ascii_lowercase.encode()): shifted_alphabet = alphabet[shift:] + alphabet[:shift] return plaintext.translate(plaintext.maketrans(alphabet, shifted_alphabet)) dest_dir = os.path.dirname(filename) chunksize = 1 << 15 with open(filename, 'rb') as file, \ NamedTemporaryFile('wb', dir=dest_dir, delete=False) as tmp_file: while True: # encrypt chunk = file.read(chunksize) if not chunk: # EOF break tmp_file.write(caesar_bytes(chunk, caesar_shift)) os.replace(tmp_file.name, filename) 

Entrada

 abc def ABC DEF 

Salida

 def ghi ABC DEF 

Para volver a convertir la salida, establezca caesar_shift = -3 .

Para abrir un archivo en modo binario, use el open("filena.me", "rb") . Nunca he usado el comando personalmente, pero eso debería proporcionarle la información que necesita.