Python 2.7在Sublime Text 3上构建时不能打印'\uFFFD'字符。

4

问题。

我正在使用在Sublime Text 3上构建的Python 2.7,并且在打印输出时遇到了问题。
在某些情况下,对于'\uFFFD' - 'REPLACEMENT CHARACTER',我得到了一个相当令人困惑的输出。


例如:

print u'\ufffd' # should be '�' - the 'REPLACEMENT CHARACTER'
print u'\u0061' # should be 'a'
-----------------------------------------------------
[Finished in 0.1s]

在顺序反转后:
print u'\u0061' 
print u'\ufffd'
-----------------------------------------------------
a
�
[Finished in 0.1s]

所以,Sublime可以打印出“�”字符,但不知何故在第一种情况下无法打印出来。
而输出结果与语句顺序的依赖关系似乎非常奇怪。
替换字符的问题通常会导致打印行为非常不可预测。
例如,我想使用错误替换打印解码后的字节:
cp1251_bytes = '\xe4\xe0' # 'да' in cp1251 
print cp1251_bytes.decode('utf-8', errors='replace')
-----------------------------------------------------
��
[Finished in 0.1s]

让我们替换字节:

cp1251_bytes = '\xed\xe5\xf2' # 'нет' in cp1251
print cp1251_bytes.decode('utf-8', errors='replace')
-----------------------------------------------------
[Finished in 0.1s]

并添加一个额外的打印语句:

cp1251_bytes = '\xed\xe5\xf2' # 'нет' in cp1251 
print cp1251_bytes.decode('cp1251') 
print cp1251_bytes.decode('utf-8', errors='replace')
-----------------------------------------------------
нет
���
[Finished in 0.1s]

以下是实施其他测试用例的示例:

enter image description here


总结一下,描述的打印行为有以下模式:
  • 它取决于打印语句中'\ufffd'字符的奇偶数
  • 它取决于打印语句的顺序
  • 它取决于具体的构建运行

  • 我的问题:

  • 为什么会发生这种情况?
  • 如何解决这个问题?

  • 我的Python 2.7 sublime-build文件:
    {   
        "cmd": ["C:\\_Anaconda3\\envs\\python27\\python", "-u", "$file"],
        "file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",
        "selector": "source.python",
        "env": {"PYTHONIOENCODING": "utf-8"}
    }
    

    如果单独安装了Python 2.7而不是Anaconda,则行为完全相同。


    对我来说,它似乎在标准的Python 2.7中可以工作;也许Anaconda正在做一些不当的事情? - OdatNurd
    @OdatNurd,我刚刚尝试了全新安装的标准Python 2.7,但得到了与问题中相同的行为。 - MaximTitarenko
    @MaximTitarenko,很高兴你得到了答案。 - HerrGanzorig
    @HerrGanzorig,也很高兴。感谢您提供的赏金 - 它使问题复活了! - MaximTitarenko
    2个回答

    1
    我已经重现了你的问题,并找到了一个在我的平台上有效的解决方案:从你的cmd构建配置选项中删除-u标志

    我不确定为什么会起作用,但似乎是控制台解释包含多字节字符的无缓冲数据流产生的不良交互。以下是我发现的:

    • -u 选项将Python的输出切换为无缓冲
    • 这个问题并不特定于替换字符。我曾遇到过其他字符(如"あ"(U+3042))的类似行为。
    • 其他编码也会出现类似的错误结果。设置"env": {"PYTHONIOENCODING": "utf-16be"}将导致print u'\u3042'输出0B
    那个将编码设置为UTF-16BE的最后一个示例说明了我认为正在发生的事情。控制台因为输出是未缓冲的,所以一次只接收一个字节。因此,它首先接收到0x30字节。然后控制台确定这不是有效的UTF-16BE,决定改用ASCII并输出0。当然,它紧接着就接收到下一个字节,并按照相同的逻辑输出B
    使用UTF-8编码时,控制台接收到的字节不可能被解释为ASCII,因此我认为控制台在正确解释未缓冲流方面做得稍微好一些,但仍然遇到了您问题指出的困难。

    确认 - 感谢您的建议,程序在所有示例中都可以正常工作。 - MaximTitarenko

    1

    编辑-1 - 使用带BOM的UTF8

    在Windows系统中,BOM变得很重要。因此,您需要使用以下类型的构建配置:

    {   
        "cmd": ["F:\\Python27-14\\python", "-u", "$file"],
        "file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",
        "selector": "source.python",
        "env": {
            "PYTHONIOENCODING": "utf_8_sig"
        },
    }
    

    然后它在Windows上也对我正常工作了。

    build settings

    correct output

    原始答案

    我检查了这个问题,在Python 2.7和Sublime Text中没有遇到同样的问题。唯一的变化是我不得不在文件顶部添加# -*- coding: utf-8 -*-。这似乎是这个问题缺失的部分。

    # -*- coding: utf-8 -*-
    
    print u'\u0061' # should be 'a'
    print u'\ufffd' # should be '�' - the 'REPLACEMENT CHARACTER'
    

    之后的反转没有影响

    print 1

    print 2

    你可以在 Why declare unicode by string in python?">这里 查看有关此必需标头的更多详细信息。
    以下是以上链接的摘要:
    当您指定 # -*- coding: utf-8 -*- 时,您告诉 Python 您保存的源文件是 utf-8。Python 2 的默认值为 ASCII(对于 Python 3,它是 utf-8)。这只影响解释器读取文件中的字符的方式。

    感谢你的贡献,但你的假设绝对不是原因。我没有在第一个示例中指定编码声明,因为我觉得它的存在是显而易见的。顺便说一下,如果你尝试在没有编码声明的情况下运行第一个示例,你会得到SyntaxError - 因为有非ASCII字节\xef,从而导致注释中的字符开始。因此,没有编码声明就无法从我的示例中获得结果。最后 - 你可以看到我在问题的gif顶部使用了# coding: utf-8 - MaximTitarenko
    还有几件事情。在您的构建配置中,可以尝试 "env": {"PYTHONIOENCODING": "utf8"}。所以是 utf8 而不是带有 - 的。不确定它是否会有所帮助,但值得一试。此外,请在构建配置中添加另一个字段,命名为 encoding: <value>,并尝试使用 utf8utf-8cp1252 等值,看看是否有任何帮助。 - Tarun Lalwani
    使用非Anaconda的Python 2.7版本和构建{ "cmd": ["C:\\Python27\\python", "-u", "$file"], ...,行为完全相同。如果我在终端(Ubuntu)中运行程序,它可以正常工作。Python 3.6 + Sublime Text也能正常工作。 - MaximTitarenko
    "env": {"PYTHONIOENCODING": "utf8"} changes nothing, as well as adding: "encoding": "utf8". However, "encoding": "cp1252" prints out for every replacement character the following: �, which is actually the result of: '�'.decode('cp1252').encode('utf-8') - MaximTitarenko
    我想我弄清楚了。你需要使用"env": {"PYTHONIOENCODING": "utf_8_sig"}。请尝试一下,然后让我知道它是否有效。 - Tarun Lalwani
    显示剩余4条评论

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