关于UTF8子串长度的困惑

时间:2017-07-24 21:21:17

标签: python-3.x utf-8 byte-order-mark

有人可以帮我处理字节顺序标记( BOM )字节与XHTML文件第一行中的UTF8字符吗?

我将XHTML文件打开为UTF8文本:

inputTopicFile = open(inputFileName, "rt", encoding="utf8")

如此十六进制编辑器所示,该UTF8编码的XHTML文件的第一行以三字节UTF8 BOM EF BB BF开头:

Hex-editor view of data in a UTF8 file

我想从我认为相当于字符串中的三个初始字符位置[0:2]的内容中删除UTF8 BOM。所以我尝试了这个:

firstLine = firstLine[3:]

无效 - 字符<?不再出现在结果行的开头。

所以我做了这个实验:

for charPos in range(0, 3):
    print("charPos {0} == {1}".format(charPos, firstLine[charPos]))

打印出来:

charPos 0 == 
charPos 1 == <
charPos 2 == ?

然后我将.encode添加到该循环中,如下所示:

for charPos in range(0, 3):
    print("charPos {0} == {1}".format(charPos, eachLine[charPos].encode('utf8')))

哪位给了我:

charPos 0 == b'\xef\xbb\xbf'
charPos 1 == b'<'
charPos 2 == b'?'

显然Python 3以某种方式“知道”3字节BOM是单个非字符数据单元吗?这意味着人们不能尝试处理行中的前三个8位字节(?),就好像它们是UTF8字符一样?

此时我知道我可以通过指定firstLine = firstLine[1:]来“欺骗”我的代码。但这样做似乎是错误的(?)

那么在仅处理UTF8字符的过程中丢弃UTF8字符串中前三个BOM字节的正确方法是什么?

编辑:根据Anthony Sottile发表的评论,解决方案在我打开源XHTML文件时就像使用encoding="utf-8-sig"一样简单:

inputTopicFile = open(inputFileName, "rt", encoding="utf-8-sig")

剥离了BOM。瞧!

1 个答案:

答案 0 :(得分:1)

正如您在编辑中提到的那样,您可以使用utf8-sig编码打开文件,但要回答您的问题原因:

Python 3区分字节字符串(具有b前缀的字符串)和字符串(不带b前缀),以及尽可能使用字符串。字节字符串与实际字节一起使用;字符串与Unicode代码点一起使用。 BOM是单个代码点,U + FEFF,因此在常规字符串中,Python 3会将其视为单个字符(因为 是单个字符)。当您调用encode时,将字符串转换为字节字符串。

因此,您所看到的结果正是您应该拥有的结果:Python 3确实知道什么算作单个字符,只有在您调用encode之后才能看到它。