Python2.7和Unicode

2024-04-27 18:16:33 发布

您现在位置:Python中文网/ 问答频道 /正文

我在写一个胖文件系统解析器。那些不知道或不太关心这个主题的人,由于对原始FAT的扩展,FAT文件名处理非常复杂。文件总是有大写的短名称。如果文件名实际上很短,并且是大写的,那么这是文件唯一的名称,比如食品.TXT. 相反,如果文件名超过8个字符,或者名称是大写和小写的混合,则会有另一个名称,即16位unicode。在

当构造一个文件的路径时,它当然是由每个子目录的名称和文件名组成的。我需要将这些名称与来自sqlite数据库的其他名称进行比较。我的比较从不匹配,当我更深入地查看数据时,我发现文件名字符串就是这样

/FOO/PUP/M^@o^@u^@n^@t^@i^@n^@g^@。。。在

因为我必须使用长的unicode名称(如果有的话)。当一些字符是8位,有些是16位时,我无法将任何内容与该字符串匹配。我无法摆脱16位unicode,因为文件名可能包含unicode字符。在

我建议的解决方案是强制所有内容都使用16位unicode,并尝试对它们进行比较。我该怎么做?如果我声明unicode("FOO", errors="strict"),我仍然只得到8位字符(当遇到文件名为0xE5的已删除文件时,由于strict而崩溃)。在

或者有没有办法把16位unicode字符转换成西方的ascii码?这样会更好。在


Tags: 文件字符串名称解析器食品内容主题foo
2条回答

unicode(byte_string, errors="strict")使用当前默认编码对传递给它的字节字符串进行解码。这不太可能与您的数据编码匹配。在西方窗口,它通常是iso8859-1,也就是拉丁语-1。但脂肪中的数据是UTF-16,little-endian。在

因此,您应该使用encoding参数显式指定正确的编码:

unicode (byte_string, errors='strict', encoding='utf_16_le')

只需确保用Unicode进行所有比较。当然,你必须知道数据的原始编码。以下是相同Unicode字符的四种不同编码:

#!python3
s1 = b'\xce\xd2\xca\xc7\xc3\xc0\xb9\xfa\xc8\xcb'
s2 = b'\xe6\x88\x91\xe6\x98\xaf\xe7\xbe\x8e\xe5\x9b\xbd\xe4\xba\xba'
s3 = b'\x11b/f\x8e\x7f\xfdV\xbaN'
s4 = b'\x11b\x00\x00/f\x00\x00\x8e\x7f\x00\x00\xfdV\x00\x00\xbaN\x00\x00'

u1 = s1.decode('chinese')
u2 = s2.decode('utf8')
u3 = s3.decode('utf-16le')
u4 = s4.decode('utf-32le')

assert(u1==u2==u3==u4)

尽快将每个文本字符串转换为Unicode。当再次写出数据时,将其编码为您首选的编码。在

关于使用\xE5删除的文件,首先处理原始数据以确定它是否是已删除的条目。无需将已删除的文件处理为Unicode:

^{pr2}$

编辑

今天下午我很无聊,这里有一个简短的FAT32解析器。它并没有严格遵循FAT32 spec这仅仅是为了说明解码:

#!python3
import binascii
import struct

# struct module unpacking formats
SHORT_ENTRY = '<11s3B7HL'     # 12 fields described in FAT32 spec
LONG_ENTRY  = '<B10s3B12sH4s' # 8 fields described in FAT32 spec

# attribute bit values (byte offset 11) 
ATTR_READ_ONLY = 0x01
ATTR_HIDDEN    = 0x02
ATTR_SYSTEM    = 0x04
ATTR_VOLUME_ID = 0x08
ATTR_DIRECTORY = 0x10
ATTR_ARCHIVE   = 0x20
LAST_LONG_ENTRY = 0x40
ATTR_LONG_NAME = ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID
ATTR_LONG_NAME_MASK = ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID | ATTR_DIRECTORY | ATTR_ARCHIVE

# A few entries from a FAT32 root directory (32 bytes per row)
data = binascii.unhexlify('''
  42 FC 00 69 00 6E 00 6F 00 2E 00 0F 00 D9 6A 00 70 00 67 00 00 00 FF FF FF FF 00 00 FF FF FF FF 
  01 6C 9A 4B 51 6D 00 61 00 F1 00 0F 00 D9 61 00 6E 00 61 00 20 00 70 00 65 00 00 00 6E 00 67 00 
  4D 41 A5 41 4E 41 7E 31 4A 50 47 20 00 89 6D 8B FE 40 69 43 00 00 C7 7D 8B 3F 03 00 04 06 7D 00 
  41 11 62 2F 66 8E 7F FD 56 BA 4E 0F 00 DC 2E 00 74 00 78 00 74 00 00 00 FF FF 00 00 FF FF FF FF 
  46 32 33 33 7E 31 20 20 54 58 54 20 00 4B BA 7B 69 43 69 43 00 00 BB 7B 69 43 00 00 00 00 00 00 
'''.strip().replace(' ','').replace('\n',''))

# Long names are built up from multiple entries, so start empty
raw_long = b''

# Iterate through the 32-byte entries in the data
for offset in range(0,len(data),32):
    raw_entry = data[offset:offset+32]

    # Entries that start with 0xE5 are deleted.
    # An entry that starts with zero indicates no more entries
    if raw_entry[0] == 0xE5: continue
    if raw_entry[0] == 0: break

    if raw_entry[11] & ATTR_LONG_NAME_MASK == ATTR_LONG_NAME:
        # Long entries are found last-to-first and are in three parts
        # per entry.  Concatenate the parts and prepend to entries
        # found so far.
        entry = struct.unpack_from(LONG_ENTRY,data,offset)
        raw_long = entry[1] + entry[5] + entry[7] + raw_long
    else:
        entry = struct.unpack_from(SHORT_ENTRY,data,offset)
        # If the short entry is a volume ID, skip it.
        if entry[2] == ATTR_VOLUME_ID: continue
        # Unpack and decode 8.3 filename in OEM
        # character set.
        basename = entry[0][:8].decode('cp437').rstrip(' ')
        ext = entry[0][8:].decode('cp437').rstrip(' ')
        # Decode and strip the current long name value of padding.
        long_name = raw_long.decode('utf-16le').rstrip('\uffff').rstrip('\0')
        print('{:8}.{:3} - {}'.format(basename,ext,long_name))
        raw_long = b'' # Reset the long name to empty

从支持UTF-8的IDE(不是Windows控制台)输出:

MAÑANA~1.JPG - 马克mañana pengüino.jpg
F233~1  .TXT - 我是美国人.txt

相关问题 更多 >