如何更改文件中的字节?

时间:2015-02-14 22:18:00

标签: python unicode binary ascii encode

我正在制作加密程序,我需要以二进制模式打开文件来访问非ascii和不可打印的字符,我需要检查文件中的字符是字母,数字,符号还是不可打印的字符。这意味着如果字节(当它们被解码为ascii时)匹配任何这些字符,我必须逐个检查:

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

我想我可以将上面的这些字符编码为二进制,然后将它们与字节进行比较。我不知道该怎么做。

  

P.S。对不起,英语和二进制误解不好。 (我希望你   知道我的意思是字节,我的意思是二进制模式中的字符   此):

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

2 个答案:

答案 0 :(得分:3)

Python中有两种主要的字符串类型:表示二进制数据的字节串(字节序列)和表示人类可读文本的Unicode字符串(一系列Unicode代码点)。将一个转换为另一个(☯)很简单:

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

如果以二进制模式打开文件,例如'rb',则file.read()返回字节字符串(bytes类型):

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

有几种方法可用于对字节进行分类:

  • 字符串方法,例如bytes.isdigit()

    >>> b'1'.isdigit()
    True
    
  • 字符串常量,例如string.printable

    >>> import string
    >>> b'!' in string.printable.encode()
    True
    
  • 正则表达式,例如\d

    >>> import re
    >>> bool(re.match(br'\d+$', b'123'))
    True
    
  • curses.ascii模块中的分类函数,例如curses.ascii.isprint()

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

bytearray是一个可变的字节序列 - 与字节字符串不同,您可以在其中更改它,例如,将每个大写的第3个字节小写:

>>> 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_')

注意:b'ad'是小写但b'_'保持不变。


要在地方修改二进制文件,您可以使用mmap模块,例如,在'file'中的每一行中小写第4列:

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

注意:Python 2和3 API在这种情况下有所不同。该代码使用Python 3。

输入

ABCDE1
FGHIJ
ABCDE
FGHI

输出

ABCDE1
FGHiJ
ABCDE
FGHi

注意:第4列在第2行和第4行变为小写。


通常,如果要更改文件:从文件中读取,写入对临时文件的修改,并在成功时将临时文件移动到原始文件中:

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

输入

abc
def
ABC
DEF

输出

def
ghi
ABC
DEF

要将输出转换回来,请设置caesar_shift = -3

答案 1 :(得分:0)

要以二进制模式打开文件,请使用open("filena.me", "rb")命令。我从来没有亲自使用过该命令,但这应该可以为您提供所需的信息。