"ANSI as UTF-8"是什么,如何让fputcsv()生成带BOM的UTF-8编码?

19

我编写了一个PHP脚本,生成之前由另一个过程生成的CSV文件。之后,CSV文件必须由另一个进程导入。

旧CSV文件的导入正常工作,但是当导入新的CSV文件时,会出现特殊字符问题。

当我使用Notepad ++打开旧的CSV文件时,它说编码为UTF-8,而当我使用它打开新的CSV文件时,它说它们的编码为“ANSI as UTF-8”。

两者有什么区别?

如何使fopen和fputcsv使用'纯粹的' UTF-8编码?

谢谢!


1
ANSI 是美国国家标准学会。我想你是指 ASCII。 - Gumbo
9
不,Notepad++ 使用“ANSI”一词与 Microsoft 一样,表示底层操作系统的默认8位编码。但是,“ANSI作为UTF-8”是 Notepad++ 自己创造的奇怪术语。 - Alan Moore
@Petruza:这个问题实际上与CSV、fopen()甚至PHP都没有关系——它完全是关于Notepad++的。我已经相应地更改了标题。 - Alan Moore
@Alan Moore:我不同意你的编辑。Petruzas的主要问题是某个过程对两个几乎相同的CSV文件处理方式不同,他的主要问题是这种差异可能是什么。Notepad++只是他用来检查差异的工具,所以我认为你的新标题有点误导。 - Henrik Opel
@Henrik Opel:你说得对,我在Notepad++的事情上有些过头了。 - Alan Moore
是的,那完全不是我要求的。无论如何,我通过使用utf_decode()来修复它,因此将utf-8输入更改为ANSI。例如,这对于西里尔字母是行不通的,但这里的问题是重音拉丁字母。谢谢大家!(@Gumbo:我的意思就是我所说的话,我知道ANSI是什么) - Petruza
4个回答

42

文件没有问题。"ANSI as UTF-8" 表示没有字节顺序标记,但 Notepad++ 通过分析字节模式明确将编码识别为 UTF-8。我通过创建一个包含俄文、希腊文和波兰文本的文件,并将其保存为没有字节顺序标记的 UTF-8 来测试了这一点。下面是文件:

# Russian
Следующая

# Greek
Επόμενη

# Polish
Więcej

我在另一个编辑器(EditPad Pro)中进行了操作,并使用十六进制模式确保没有BOM。当我在NPP中打开它时,它显示为“ANSI作为UTF-8”编码,并且所有字符都正确显示。然后,仍然在十六进制模式下,我删除了第一个俄语字符的第一个字节。当我再次在NPP中打开它时,它显示为“ANSI”编码,并将文本的非ASCII部分显示为乱码

; Russian
¡Ð»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ

; Greek
Επόμενη

; Polish
Więcej

回到EditPad,这次我添加了BOM,但没有修复西里尔字符。 NPP这次报告编码为“UTF-8”,除了第一个俄文字符显示不正确外,其他一切都正常显示,如下所示。“A1”是该字符在UTF-8中应该是第二个字节的十六进制表示。它以反色方案显示,以指示错误。

# Russian
A1ледующая

# Greek
Επόμενη

# Polish
Więcej

总结一下:如果没有BOM(字节顺序标记),Notepad ++会查找不能表示ASCII字符的字节,因为它们的值大于127(或7F十六进制)。如果找到任何这样的字节,但它们都符合UTF-8所要求的模式,则将文件解码为UTF-8,并在状态栏中报告编码为“ANSI as UTF-8”。

但是,如果它发现了一个不符合UTF-8规则的字节,它会将文件解码为“ANSI”,也就是底层平台的默认单字节编码。如果您的文件已经损坏,那么你看到的就是这种情况。

编辑:尽管您的文件没有BOM也是有效的,但您可以通过手动写入三个字节"EF BB BF"来添加BOM - 但应该有更好的方法。您现在是如何生成内容的?因为它确实是UTF-8,其中至少有一个非ASCII字符;否则,NPP将其报告为“ANSI”。

另一个需要考虑的可能性是:如果您对消费CSV文件的过程有任何影响力,也许可以将其配置为期望使用没有BOM的UTF-8。从技术上讲,任何能够解码有BOM的UTF-8但不能解码无BOM的UTF-8的软件都是有问题的。 Unicode联盟实际上不赞成使用UTF-8 BOM,尽管似乎没有人在听。


1
这是一个关于“ANSI as UTF-8”主题的良好且清晰的解释,但只是部分答案(至少在您编辑问题标题之前是如此;)。我们已经更简要地涵盖了该主题(请参见下面我的答案的评论),因此剩下的主要问题是缺少/存在BOM是否会有所不同,如果有,如何修复它。 - Henrik Opel
3
对我来说,Notepad++中的“ANSI作为UTF-8”的愚蠢似乎是唯一的问题 — 如果不是这个问题,它做得很好地掩盖了这个问题。但你是对的,我忽略了回答问题的第二部分。 - Alan Moore
+1 对于 Mojibake - 实际上我遇到的怪事有个专门的词汇。 - Greg Woods
1
“ANSI as UTF-8”是具有误导性的。 ANSI与UTF-8不兼容。 ASCII(0-127)与UTF-8兼容。 ANSI范围(128-255)扩展了ASCII,但在UTF-8中是控制字符,指示下一个字节应从哪个页面查找。 “ASCII as UTF-8”可能更正确,但仍然不准确。它只是没有BOM的UTF-8。 - thomthom
我本来要报告这个问题,但是这是你的问题,@thomthom,我错了吗?xD - Áxel Costas Pena

6
根据与Notepad++相关的帖子这里这里,'ANSI as UTF-8'表示UTF-8 没有 BOM,而纯粹的'UTF-8'表示带有BOM的UTF-8。因此,可能读取CSV的过程需要字节顺序标记才能正确将CSV作为UTF-8读取。
但在此之前,请确保您的脚本实际上写入了UTF-8!当您在Notepad++中打开新的CSV文件(并且它显示为“ANSI as UTF-8”)时,所有“特殊”字符是否都正确显示?如果不是,则需要调整您的脚本以实际编写UTF-8;如果是,则检查BOM差异。

谢谢!是的,在Notepad++中,这两个CSV文件都正确显示了特殊字符。 - Petruza
1
好的,那么可能是缺少BOM。您可以在Notepad++中手动添加一个(转换为带BOM的UTF-8),然后检查是否解决了问题。如果是这样,请参见http://php.net/manual/en/function.utf8-encode.php#68211了解如何在PHP中创建BOM。 - Henrik Opel
2
现在Notepad++显示“UTF-8 without BOM”而不是令人困惑的“ANSI as UTF-8”。好的改变! :) - Valentino Vranken

1

0
值得注意的是,如果您将PHP文件格式化为UTF-8,则ANSI作为UTF-8(即没有BOM的UTF-8)非常有用。如果您的PHP文件向浏览器输出HTML,则BOM将包含在HTML输出中,而w3c验证器明确警告不要这样做:

在UTF-8文件中找到字节顺序标记。

已知UTF-8编码文件中的Unicode字节顺序标记(BOM)会对某些文本编辑器和旧版浏览器造成问题。您可能希望考虑避免使用它,直到它得到更好的支持。

此外,我发现BOM会让Firefox的Firebug混淆,现在它认为所有的<head>内容实际上都在<body>标签中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接