运行代码时,为什么在'±'前面打印'Â'?

时间:2019-07-16 16:20:51

标签: python string unicode ascii

我正在尝试编写一个非常简单的输出语句,将其输出到一个csv文件中。它只是说明了数据的偏差余量,因此我使用的是“±”符号,因此它将显示为“ 5 ft / s ^ 2±2.4%”。

我正在使用Python3。我尝试了三种使用“±”符号的方法:ascii,unicode,然后直接将字符粘贴到编辑器中。见下文

val1 = 3.2
val2 = 2.4

s1 = val1 + "ft/sec^2 " + chr(241) + val2 + "%"
s2 = val1 + "ft/sec^2 " +  u'\u00B1' + val2 + "%"
s3 = val1 + "ft/sec^2 ±" + val2 + "%"

但是,这三种方法的输出对我来说总是相同的...

3.2ft/sec^2 ±2.4%

此“”继续显示。我完全没有编码方面的经验。我搜索了一些情况,这些情况似乎与我的情况有关,但了解不够,无法为我的具体情况找到解决方案。

我正在使用pandas DataFrame收集数据,然后使用.to_csv()方法创建csv。它的文档指出它默认为'utf-8'编码。

这里有7行为我重现了同样的问题。

import pandas as pd 

df = pd.DataFrame(columns=['TestCol'])
df['TestCol'] = ["Test1: " + chr(241),
    "Test2: " + u'\u00B1',
    "Test3: " + "±"]
df.to_csv('TestExample.csv', index=False, encoding='utf-8')

在我的CSV中,我得到了一个看起来像这样的列:

TestCol
Test1: ñ
Test2: ±
Test3: ±

感谢任何帮助,解释和知识!

3 个答案:

答案 0 :(得分:3)

Excel会在打开.csv文件时采用Windows编码。这种编码取决于语言/国家/地区,但是在英语和西欧国家/地区是cp-1252,它与ISO-8859-1(也称为“ latin1”)非常相似。

此编码每个字符使用一个字节。这意味着它最多允许256个不同的字符(实际上,它们少于256个,因为某些代码是为控制和不可打印的字符保留的。)

Python3使用Unicode表示字符串。 Unicode没有“仅256”个符号的限制,因为它内部使用〜20位。实际上,Unicode可以代表世界上任何语言的任何字符(甚至包括世界之外的某些语言)。

问题是,当必须将Unicode写入文件(或通过网络传输)时,必须将其“编码”为字节序列。实现此目的的一种方法以及许多领域的当前标准是“ UTF-8”。

UTF-8编码每个字符使用可变数目的字节。它被设计为与ASCII兼容,因此ASCII表中的任何符号都用一个字节表示(与其ASCII码一致)。但是任何不在ascii中的字符都将需要超过1个字节来表示。特别是,字符±(编码点U+00B1或177)以UTF-8编码时,需要两个字节的十六进制值c2b1

Excel读取这些字节时,由于它假定cp-1252编码每个字符使用一个字节,因此它将序列c2b1解码为两个单独的字符。第一个被解码为Â,第二个被偶然地解码为±

  

注意顺便说一下,unicode ñ(代码点U+00F1或241)也以UTF-8编码为两个字节,值分别为c3,{ {1}},当解码为cp-1252时显示为b1。请注意,第一个是ñ,而不是Ã,但是第二个是(通常是再次)Â

解决方案是向熊猫表明在写入文件时应使用cp-1252编码:

±

当然,这有潜在的问题。由于“ cp-1252”最多只能表示256个符号,而Unicode最多可以表示1M个符号,因此,数据框中的某些字符串数据可能会使用“ cp-1252”中无法表示的任何字符。在这种情况下,您会遇到编码错误。

此外,在用熊猫读回df.to_csv("file.csv", encoding="cp1252") 时,您必须指定编码,因为熊猫假定它是UTF-8。

有关.csv

的更新

其他答案和一些注释涉及utf-8-sig编码,这将是另一种有效的(也许更可取的)解决方案。我会详细说明这是什么。

UTF8不是将Unicode转换为字节序列的唯一方法,尽管它是几种标准中推荐的一种。另一个受欢迎的选择是(?) UTF-16 。在这种编码中,所有Unicode字符都被编码为16位值(其中某些不能用这种方式表示,但是可以通过对某些字符使用两个 16位值来扩展该集合)。

每个字符使用16位而不是8位的问题是 endianess 是相关的。由于16位不是内存,网络和磁盘运行的基本单位,因此当您将16位值写入或发送到内存,网络或磁盘时,实际上会发送两个字节。这些字节的发送顺序取决于体系结构。例如,假设您需要在磁盘中写入16位数字"utf-8-sig"(以十六进制表示)。您必须将其“分解”为66ff66,并确定哪个首先写入。磁盘中的序列可以是ff66(称为<大>大端顺序)或ffff(称为< em>小端序)。

如果您使用的是 little-endian 体系结构(例如Intel),则磁盘中字节的默认顺序将与 big-endian 体系结构不同。当然,问题是当您尝试在一台体系结构与创建该文件的体系结构不同的计算机上读取文件时。您可能会错误地以66结束这些字节,这可能是一个不同的Unicode字符。

因此,必须以某种方式在文件中包含有关创建时使用的 endianity 的信息。这就是所谓的BOM(字节顺序标记)的作用。它由Unicode字符ff66组成。如果此角色是文件中的第一个字符,则在回读文件时,如果您的软件发现FEFF是第一个字符,它将知道 endianity 用于读取该字符。文件与写入文件时使用的文件相同。但是,如果找到FEFF(交换了顺序),它将知道存在一个 endianity 不匹配,然后它将在读取时交换每对字节,以获取正确的Unicode字符。

顺便说一句,Unicode标准没有一个字符为FFFE的字符,以避免在读取BOM时造成混淆。如果您在一开始发现FFFE,则表示字节序错误,必须交换字节。

这都不涉及UTF-8,因为此编码使用字节(而不是16位)作为信息的基本单位,因此不受字节顺序问题的影响。不过,您可以使用UTF-8编码FEFF(这将导致3个字节的序列,其值分别为FFFEEFBB)并将其作为第一个字符写入在文件中。当您指定BF编码时,Python便会这样做。

在这种情况下,其目的不是帮助确定字节序,而是充当一种“指纹”,这有助于读取文件的软件猜测所使用的编码为UTF-8。如果软件发现“魔术值” utf-8-sigEFBB作为文件中的前3个字节,则可以得出结论,该文件存储在UTF-8中。这三个字节被丢弃,其余的则从UTF-8解码。

特别是,Microsoft Windows在其大多数软件中都使用了此技术。显然,在Excel中也可以,因此,总结一下:

  • 您使用BF
  • 编写了csv
  • Excel读取文件并在开始时找到df.to_csv("file.csv", encoding="utf-8-sig")EFBB。因此它将丢弃这些字节,并为文件的其余部分假定为utf-8。
  • 以后BFc2出现在文件中时,它被正确解码为UTF-8以产生b1

这具有可以在任何Windows计算机上工作的优势,而不管它使用的是什么代码页(cp1252用于西欧,其他国家也可以使用其他代码页,但是Unicode和UTF-8是通用的。) / p>

潜在的问题是,如果您尝试在非Windows计算机上读取此csv,请执行以下操作。可能发生的情况是,前“魔术字节” ±EFBB对读取它的软件毫无意义。然后,您可能会在文件开头以“虚假”字符结尾,这可能会导致问题。如果读取文件的软件采用UTF-8编码,则前三个字节将被解码为Unicode字符BF,但它们不会被丢弃。该字符是不可见的,宽度为零,因此无法通过任何编辑器“看到”它,但是仍然可以在其中看到它。如果读取文件的软件采用任何其他编码,例如“ latin1”,则前三个字节将被错误地解码为FFFE,并且在文件的开头将可见。

如果您使用python读回此文件,则必须再次指定编码,以使python丢弃这三个初始字节。

答案 1 :(得分:2)

您正在将UTF-8写入文件,但是您用来查看该文件的任何内容都将其查看为latin-1(或类似的Windows cp1252)。您可以尝试使用open encoding='utf-8-sig'来写入要写入的文件,这会将BOM放在文件的开头,以便应用程序可以将其识别为UTF-8。或者,您可能只是告诉您的查看器程序将其解释为UTF-8。我强烈建议您不要latin-1或类似的文字编写,因为这将使文本不可移植到具有其他语言环境的系统,而无需明确告诉人们如何对其进行解码。

答案 2 :(得分:1)

s3包含UTF8编码的值,其中±(U + 00B1)的UTF8编码为\xc2\xb1。但是,您的终端将字节解释为ISO-8859编码的文本,而不是UTF-8编码的文本。在ISO-8859中,代码点C2为(您现在可能已经猜到了)“”,代码点B1为“±”。实际上,对于介于U + 00A0和U + 00BF(含)之间的所有Unicode值,其UTF-8编码的第二个字节与它们的Unicode代码点重合。另外,对于代码点00-FF,ISO-8859-1与Unicode一致。