-3

FAT ファイル システム パーサーを作成しています。このトピックをあまり知らない、またはあまり気にしない人のために説明すると、FAT ファイル名の処理は、元の FAT の拡張により複雑になっています。ファイルには、常に大文字の短い名前があります。ファイル名が実際には大文字で短い場合、FOO.TXT のように、これがファイルの唯一の名前です。対照的に、ファイル名が 8 文字を超える場合、または名前に大文字と小文字が混在している場合は、16 ビット Unicode である別の名前があります。

ファイルへのパスを作成するときは、もちろん、各サブディレクトリの名前と最後にファイル名で構成されます。これらの名前を、sqlite データベースに由来する他の名前と比較する必要があります。私の比較は決して一致しません。データをさらに詳しく調べたところ、ファイル名の文字列がたとえば次のようになっていることがわかりました

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

使用可能な場合は、長いユニコード名を使用する必要があるためです。一部の文字が8ビットで一部が16ビットの場合、その文字列と何も一致させることができません。ファイル名に Unicode 文字が含まれている可能性があるため、16 ビット Unicode を取り除くことはできません。

私が提案する解決策は、すべてを 16 ビット Unicode に強制し、それらを比較することです。どうすればいいですか?私が述べた場合unicode("FOO", errors="strict")、私はまだ8ビット文字しか取得しません(そして、ファイル名に0xE5を含む削除されたファイルが発生するとすぐに、strictによるクラッシュが発生します)。

または、16 ビットの Unicode 文字を西洋の ascii に変換する方法はありますか? これはさらに良いでしょう。

4

2 に答える 2

3

すべての比較を Unicode で行うようにしてください。もちろん、データの元のエンコーディングを知っている必要があります。以下は、同じ Unicode 文字の 4 つの異なるエンコーディングです。

#!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 に処理する必要はありません。

if rawdata[0] = 0xE5:
    print('deleted')
else:
    print(rawdata.decode('utf-16le'))

編集

今日の午後は退屈だったので、短い FAT32 パーサーを紹介します。FAT32 仕様に厳密に従っているわけではありません... デコードを説明するのに十分です:

#!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
于 2013-11-09T20:58:06.553 に答える
2

unicode(byte_string, errors="strict")現在のデフォルトのエンコーディングを使用して、渡されたバイト文字列をデコードします。これがデータのエンコーディングと一致する可能性はほとんどありません。欧米の Windows では、通常 iso8859-1、別名 latin-1 になります。ただし、FAT のデータは UTF-16、リトルエンディアンです。

encodingしたがって、パラメーターを使用して明示的に正しいエンコーディングを指定する必要があります。

unicode (byte_string, errors='strict', encoding='utf_16_le')
于 2013-11-09T17:57:21.937 に答える