注释:虽然我理解对数据"准确性"的一些担忧,但我认为这对问题来说真的无关紧要。数据就是这样。假设是这样,我们会对可能的解决方案感兴趣。
--------
我需要读取一个包含具有unicode字符的行的文件。这些行包含多个列,每个列从固定位置开始。
这里有一个例子(我放了连字符来指示单个列的结束位置,连字符实际上不是字符串的一部分)。0004-1235957-A CORU¥A -ABC
在上面的例子中,列1占据前4个位置,列2占据接下来的7个位置,栏3占据接下来的15个位置,而栏4占据接下来的3个位置,依此类推
我使用以下代码来解析这一行:
line = '00041235957A CORU¥A ABCn'
formats = ('4s 7s 15s 3s')
st = struct.Struct(formats)
fields = tuple(s.decode() for s in st.unpack_from(line.encode()))
print(fields)
这是我得到的输出:
('0004', '1235957', 'A CORU¥A ', ' AB')
如果你看最后一列,它实际上读错了。原因是在unicode中,字符"¥"占用两个字节,这导致只有前14个字符(和15个字节)被读取,第4列从后面的位置被读取。
我想要的是一种读取第3列的15个字符的方法,而不是独立于数据中的编码的15个字节。即使我在线上尝试子字符串方法,我也会得到相同的行为。
请专家们对此提供一些指导意见好吗?非常感谢。
line.encode()
时,默认编码为utf-8
。正如您在下面看到的,¥
符号变成了两个字节xc2xa5
,抛出了您的计数:
>>> line = '00041235957A CORU¥A ABCn'
>>> line.encode()
b'00041235957A CORUxc2xa5A ABCn'
正如评论所提到的,你很可能一开始就对文件进行了误读,因为日元符号不太可能出现在看起来像西班牙语的单词中间。请确保您知道文件的原始编码,以便正确读取文件。考虑读取二进制文件,例如open(filename,'rb')
,并将有问题的行的原始字节作为示例发布。
下面是两个";解决方案":
import struct
line = '00041235957A CORU¥A ABCn'
st = struct.Struct('4s7s15s3s')
# encode with a single-byte encoding so the length doesn't change.
fields = tuple(s.decode('latin1') for s in struct.unpack_from(line.encode('latin1')))
print(fields)
# slice the string directly (RECOMMENDED)
fields = line[:4],line[4:11],line[11:26],line[26:29]
print(fields)
输出:
('0004', '1235957', 'A CORU¥A ', 'ABC')
('0004', '1235957', 'A CORU¥A ', 'ABC')
是否希望以可读、可重复的方式使用切片对象来按字符划分线条。
>>> s1 = slice(0, 4)
>>> s2 = slice(4, 11)
>>> s3 = slice(11, 26)
>>> s4 = slice(26, 29)
>>> slices = line[s1], line[s2], line[s3], line[s4]
>>> slices
('0004', '1235957', 'A CORU¥A ', 'ABC')
切片对象在功能上与从字符串或列表(例如s1 = line[0:4]
)中获取切片相同,但它们独立于被切片的序列。
正如评论和另一个答案中所指出的,看起来数据可能没有被正确解码。知道字节CCD_ 8被映射到"0"可能有帮助;¥"在编码cp1252、cp1253、cp1254、cp1255、cp1256、cp1258、iso8859_15、iso8859-8、iso8879_9、latin_1、palmos中,但是被映射到";ñ"在编码cp437、cp850、cp857、cp858、cp860、cp862、cp865(源)中。
str.encode
和bytes.decode
的默认编码为utf-8
(这是一种可变宽度字符编码)。请改用固定长度编码。应用utf-32
以正确处理BMP以外的字符。
import struct
line = '00041235957A CORU¥A ABCn'
formats = ('4s 7s 15s 3s')
formats32 = '<' + ''.join([str(4*int(x))+'s' for x in formats.split('s')[:-1]])
st = struct.Struct(formats32)
fields = tuple(s.decode('utf_32_le') for s in st.unpack_from(line.encode('utf_32_le')))
print(fields)
结果:。\SO\69621626.py
('0004', '1235957', 'A CORU¥A ', 'ABC')