Radialix3 Tahoma不支持活动代码页9366怎么解决?求教大神们

2881人阅读
windows操作系统(160)
最近,我在把一个Python 2的视频下载工具改写成Python 3,并添加了自己需要的YouTube支持。
在Linux下,事情进行得很顺利:所有的东西都用进行编码。Python 3里的str类型从2.x版本的ASCII字符串变成了Unicode字符串;我移除了原来代码里关于本地编码类型的判断处理部分。程序从抓取的页面上解析出视频标题部分的Unicode字符串,直接print()显示到标准输出,一切看起来很和谐。
假定我抓取的这个视频标题是中文,叫做“你好,世界”。众所周知,得益于Python良好的Unicode支持,输出它只需要简单的一句:
print('你好,世界')
在下测试这个程序时,麻烦就出现了。如果你想知道我为什么这么说,请继续看下去。
我所不了解的Windows
去年从学校拿到这台Dell笔记本时,Windows 7自然是预装在上面的。
系统语言已经设置成了英语。很快,我对瑞典语键盘的布局感到极其不适应:它的标点符号位置与英语键盘布局有很大区别,分号、冒号、单引号双引号、斜杠反斜杠这些程序员司空见惯的符号,和美式英语键盘完全不同。于是,我把键盘布局换回了习惯的英语键盘,顺便把控制面板的“区域”选项也一概从瑞典换到了英语/美国。
在很长一段时间里,除了界面是英文以外,它看起来和以往用的中文系统没什么区别:有默认的中文字体,输入法可以添加中文的。我平时用它做的,只有:上网,挂迅雷,拿IE登网银,玩Mirror's Edge,几件事而已。
文件系统是Unicode编码的,Web浏览器是支持Unicode的,偶尔用的文本编辑器也是一律设置成UTF-8的。而且我们知道,从Windows 2000起,Windows的内码实现是使用的。几乎让人快要忘了还有代码页这么一回事。
可是,如果要在英文Windows系统的命令提示符里执行这个简单的输出Unicode文本的程序:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
if __name__ == '__main__':
print('你好,世界')
Python就会跳出来一段错误:
File &c:\Python32\lib\encodings\cp437.py&, line 19, in encode
return codecs.charmap_encode(input,self.errors,encoding_map)[0]
UnicodeEncodeError: 'charmap' codec can't encode characters in position 0-1:
character maps to &undefined&
难道Python 3不是支持Unicode的吗?难道它不是跨平台的吗?
第一个问题,基本上是对的,Python 3确实支持Unicode,这种支持体现在它把所有的str字符串都作为Unicode处理这件事情上。
第二个问题,不完全,跨平台的可移植性是有条件的。Python本身是支持Unicode,但是如果遇上了非Unicode的古董环境,那就一点办法也没有。
什么叫“非Unicode的古董环境”呢……不,我说的不是DOS。这个东西,竟然就是Windows上的cmd.exe,每个人或多或少都用过的命令行环境。
cmd.exe,从MinGW到Python,基本上每个Windows下需要接触命令行的开发人员都躲不过去的东西,微软怎么就不能把它做好些?窗口大小不能随心所欲改也就算了,不能全屏显示也就算了,字体大小屏幕缓冲设置各种限制也就算了,鼠标拖拽不方便也就算了,命令行补全补不全也就算了,你好歹能把默认编码改成用Unicode吧?一个破窗口从二十年前的3.x时代沿用到今天的Windows 7,从依赖DOS的<到独立的cmd.exe,&#23612;玛这么多年了,也没见功能上有什么实质的改进,是不是在微软&#30524;里所有的程序员都在拿个白花花的IDE“做你的code”、不需要命令行了?
(在Windows已经完全使用UTF-16作为内码实现的今天,cmd.exe仍然在使用系统默认的代码页,我所能想到的唯一理由就是为了保持和以前的non-Unicode程序兼容——不过这理由也太弱了吧)
微软有功夫把Windows 8的界面做得花里胡哨,不过看样子他们是压根不打算把cmd.exe这个东西做得更好用些。不继续喷下去了,说处理问题的经过:
前面Python的错误信息里提到了个文件cp437.py。既然是cp437什么的,那就一定是Python在试图把Unicode字符串转换成用于输出的437代码页(英语/美国)时出了错。
为什么Python要把一个好端端的Unicode字符串转换成cp437呢?这很容易想通,因为程序是在cmd.exe这个终端环境下执行的。在我的英文系统上,它的活动代码页是437(英语/美国)。从代码中的Unicode字符串到输出cp437的这一步转换,是由Python解释器来实现的,所以会由Python抛出一个错误,而不是直接在控制台输出一堆乱码。
首先想到的解决方案,自然是改变当前cmd.exe的活动代码页到UTF-8 Unicode:
不幸的是,这导致Python解释器直接崩溃了:
Fatal Python error: Py_Initialize: can't initialize sys standard streams
LookupError: unknown encoding: cp65001
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
LookupError: unknown encoding: cp65001
搜了一下才发现,Python 3.2目前。话说65001代码页不就是UTF-8嘛(囧囧囧囧囧)
与其说是不支持,不如说是bug更合适些。因为执行之后Windows就跳出一个警告框说“python.exe已经停止响应”了……
于是,试着改变代码页到GBK:
结果却是:
Invalid code page
Windows声称这是一个无效的代码页。为什么?
编码是什么
好了,暂且忘记cmd.exe诸如此类令人不愉快的东西,在上试一试。
我不知道有多少Linux程序员写Python的时候会用到IDLE。对于这些习惯了终端&#43;文本编辑器的用户来说,IDLE看起来是个无关紧要的附属品,也许它的定位只是用来帮助初学者入门的一个开发环境?
不过,容易被人们忽略的一点是:IDLE本身是个跨平台的环境,这意味着它可以无条件支持Unicode(只要系统上有相应的字体),用它来解释执行程序不必受制于特定终端环境的拘束。这一点在Windows上很重要,因为cmd.exe这玩意实在是太差劲了,所以估计很多人在Windows下交互执行Python的时候还是会选择IDLE的。
进入IDLE。我们可能要关心一下这个Windows系统下面的默认编码方式是什么,Python 3里面有两个函数:
&&& sys.stdout. locale.getpreferredencoding()
第一个sys.stdout.encoding是指标准输出的编码,第二个locale.getpreferredencoding则是系统本地化设置的编码。两者是有区别的。现在我们看到,它们在当前环境下是相同的,都是默认的,也就是传说中的“ANSI”代码页。
恩,我们已经知道IDLE是一个完全跨平台的环境,所以在IDLE上输出Unicode字符可以得到和Linux环境下同样和谐的结果:
&&& print('你好,世界')
你好,世界
顺便看看“你好,世界”的UTF-8编码和GBK是什么,如果强制用其他编码方式来解码又会得到怎样的结果(后面也许会用到)。可以看到,5个全角字符在UTF-8编码下是15个字节,每个字符占3 bytes;在GBK编码下是10个字节,每个字符占2 bytes。
虽然没有什么实际的意义,不过还是可以注意到:UTF-8编码的字符是无法用GBK解码的,哪怕是乱码有时候也不行,因为可能会出现奇数字节长度,这在GBK下不合法;反之GBK编码字符亦无法用UTF-8解码,因为有无效字符&#20540;的存在。
借助IDLE看到了“你好,世界”各种编码的详细情况。现在我们可以回到cmd.exe里面看一看下面这段程序的运行结果了:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys, locale
if __name__ == '__main__':
print(sys.stdout.encoding, locale.getpreferredencoding())
print('你好,世界')
except Exception as err:
print(str(err))
首先通过chcp确认,cmd.exe的当前活动代码页是437(英语/美国),而非IDLE里的1252(ANSI)。这是由于我的Windows里对non-Unicode程序的区域设置是“英语/美国”的缘故。
程序运行的结果是:
cp437 cp1252
'charmap' codec can't encode characters in position 0-4:
character maps to &undefined&
可以看到sys.stdout.encoding实际上就是当前环境下活动代码页的&#20540;。locale.getpreferredencoding()没变,仍然是系统默认的cp1252。
之后抛出的异常是在我们预料之中的,正如此前一样,Python尝试把Unicode字符串转换成cmd终端下的cp437代码页编码。而中文字符本来就是没有对应的cp437编码的,所以Python报错。
Google一下'charmap' codec can't encode characters in position 0-4: character maps to &undefined&这个错误。在Stack Overflow上,有人提到了解决的方法:设置一个叫做PYTHONIOENCODING的环境变量。
PYTHONIOENCODING环境变量
所谓的PYTHONIOENCODING,既可以作为环境变量存在,也可以作为Python的命令行参数传递。它用于指定Python程序标准输入输出(stdin/stdout/stderr)的编码。(注意这个编码不是指源代码的编码,和Python程序开头常见的# -*- coding: utf-8 -*-是两码事)
在没有这个环境变量时
如前面所述,Python会试图把内部Unicode编码的字符串转化成当前执行程序的终端环境下所使用的编码方式(sys.stdout.encoding)后输出。对于当前代码页437的cmd.exe来说,把只含有英文数字的字符串转成cp437编码没有任何问题;但是一旦遇上了中文字符,英语/美国的437代码页里必然是找不到对应的编码的,于是Python就会报错。
如果当前代码页设成65001,Python 3.2会崩溃,这是本身实现上的。在最新的Python 3.3 beta中已经。
在设置了这个环境变量时
set PYTHONIOENCODING=utf-8
或(PowerShell下)
$env:PYTHONIOENCODING = &utf-8&
PYTHONIOENCODING指定的编码方式会覆盖原来的sys.stdout.encoding。如果将PYTHONIOENCODING设置为utf-8,那么Python在输出Unicode字符串的时候就会以UTF-8输出,相当于什么也不转换。
再次执行该Python程序,这一次Python不再尝试自动转换Unicode的中文字符到cp437中的对应字符,程序成功运行,sys.stdout.encoding变成了utf-8,字符串输出则是乱码:
utf-8 cp1252
Σ╜áσ?╜∩╝?Σ╕?τò?
这与我们之前在IDLE中将UTF-8编码的文本强制用cp437解码得到的结果是完全相同的:
&&& print(bytes('你好,世界', 'utf-8').decode('cp437'))
Σ╜áσ?╜∩╝?Σ╕?τò?
Python直接把UTF-8编码的字符串输出到了cp437代码页的终端,相当于强制用cp437来解码UTF-8文本,产生了无意义的乱码。
用文本编辑器写一个内容是“你好,世界”的文件,以UTF-8编码保存。在cmd.exe下通过type显示,结果和上面是相同的。
cmd.exe和PowerShell ISE的微妙之处对比
在当前区域设置(英语/美国)下,两者执行<显示的当前活动代码页都是437。
只有cmd下Python的sys.stdout.encoding默认是cp437(与活动代码页相同);PowerShell ISE下sys.stdout.encoding则是cp1252(ANSI)。
locale.getpreferredencoding永远是系统本身默认的cp1252,这是一个系统全局&#20540;。
cmd无法输入中文,不能正确显示文件系统中的中文文件名;PowerShell ISE能够输入中文,能显示中文文件名。
在缺少936代码页的情况下,两者都不能够通过执行脚本或type文件内容正确显示中文字符(无论是GBK还是UTF-8),会产生乱码。
为什么Windows会缺少GBK代码页?
回到最初的那个问题上来,为什么执行< 936不能切换到GBK代码页?为什么cmd.exe和PowerShell里不能正常显示中文?
这个问题让我百思不得其解。花了几个小时找到了原因,简而言之:因为Windows的“区域和语言”设置不对。
“Language for non-Unicode programs”这个选项不是简体中文,所以就不能用GBK,手动<也会告诉你该代码页无效。所以必须要在控制面板里设置成简体中文,重启后才能生效。
好吧,问题来了,为什么这里只能单选?如果我既想使用936(GBK)编码的应用程序,又想使用932(日语)编码的应用程序,难道每次都要在这里改完后再重启吗?为什么他们不能给一个详细的代码页列表让用户多选、需要时可以动态加载?
Windows设计的龌龊之处就在这里。如果你不去设置system locale为中文并重启,所有non-Unicode程序里的中文字符集都是不会出现的,只能显示成一个方框,比如cmd.exe里:
还有Vim里(set fileencodings=utf-8,gbk),GBK编码的文本和UTF-8编码的文本都一样无法显示。(按理说Vim应该不能算non-Unicode程序吧……谁知道呢?!)
改过&Language for non-Unicode programs”为中文并且重启系统之后,Vim立即显示正常:
再进cmd.exe,默认活动代码页936。这段Python程序终于也能正确输出了:
也许Windows这种蛋疼的设计是因为考虑到英文用户一般不会需要多余的Unicode和代码页字符集,这么做可以节省系统启动时间?谁知道呢,Windows用户不是最喜欢拿所谓的“启动时间快”作为衡量系统性能的指标了吗……
切换到cp65001(UTF-8 Unicode),PYTHONIOENCODING设置成utf-8,按理来说这种方式不应该出问题,但是这输出怎么看都不像是正常(如下图所示)。不想深究到底为什么了,总之Windows下面东西的复杂程度以我这种智商是永远都不能够理解的……
Python除了标准输入输出,还有……
open('文件名测试', 'w')
Python中对文件系统的操作基本上是不受默认编码影响的,只要sys.getfilesystemencoding()的结果是utf-8(现代Linux)或者mbcs(现代Windows NT系统上)。两者本质上都是Unicode编码。
文件输入输出
文件读写不属于标准I/O,因此和环境变量PYTHONIOENCODING无关。
for c in ['utf-8', 'gbk']:
with open('test_%s.txt' % c, 'w', encoding=c) as output:
output.write('你好,世界\n')
except Exception as err:
print('\nWriting to file using %s:\n' % c, str(err))
由于在open()中显式指定了中文编码方式(encoding='utf-8'或encoding='gbk'),输出“你好,世界”这样的中文文本在任何平台上都应该能够得到正确的结果。
然而对于:
with open('test_default.txt', 'w') as output:
output.write('你好,世界\n')
except Exception as err:
print('\nWriting to file using default encoding:\n', str(err))
由于没有指定编码方式,Python会自动使用系统默认的编码方式来进行输出。如果系统默认编码是cp437或cp1252,由于中文字符在这些代码页中显然不存在对应&#20540;,Python会抛出一个熟悉的错误:
File &c:\Python32\lib\encodings\cp437.py&, line 19, in encode
return codecs.charmap_encode(input,self.errors,encoding_map)[0]
UnicodeEncodeError: 'charmap' codec can't encode characters in position 0-1:
character maps to &undefined&
当然,当系统默认编码为cp936(GBK)时,无论
output.write('你好,世界')
print('你好,世界')
都可以正常工作。因为“你好,世界”这个Unicode字符串是可以被完全转换成GBK中的对应编码的。
一些总结和思考
虽然Python 3使用Unicode编码的字符串,但是在跨平台的程序中依然要取得系统的默认编码用于后续处理,因为并不是所有的终端环境都支持全部的Unicode字符集:
if sys.stdout.isatty()
default_encoding = sys.stdout.encoding
default_encoding = locale.getpreferredencoding()
无论何时,不要随心所欲地用print()向stdout输出Unicode字符串。如果某个要输出的Unicode字符(比如,中文字)在系统默认编码的字符集(比如,代码页437)上没有,Python这时就会抛出一个错误。这其实在大部分时候并不是我们想看到的局面,我们总希望即使有时会输出一些无意义的乱码,程序整体上也能正确运行。拿视频下载工具的例子来说,即使由于终端的关系有时无法正确显示视频名称,这问题并不太严重,因为程序总是可以把抓取的视频内容写入正确的文件的。
在程序中获取了系统默认的default_encoding,我们就可以强制用它来对Unicode字符串进行编码,至少避免了Python在自动转码过程中可能会抛出的错误——虽然结果可能只是得到一堆乱码。另外一种处理方式是对于这样的字符串,我们决定根本不去输出它们。
我们比较愿意看到的情况是:如果程序会输出且只会输出中文,而你假想中的Windows用户群所使用的代码页是936(GBK)——尽管在程序中使用Unicode字符串吧,这样做不会带来任何问题。
但是,如果不能确定要处理的Unicode文本会限定在哪个代码页字符集的范围当中:中文?梵文?希伯来文?阿拉伯文?还是……火星文?这个时候就必须考虑到世界上还有“编码方式差异”这回事了。当然,最好的解决方式也许是:告诉用户,去他的代码页,去他的什么437、500、936、1252……这堆诡异的数字,去他的,扔掉设计上如此糟糕、编码方式如此混乱和不一致的Windows,转投一个吧。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:2324393次
积分:23752
积分:23752
排名:第208名
原创:355篇
转载:1189篇
评论:53条
(2)(1)(12)(3)(25)(37)(9)(32)(19)(7)(5)(11)(26)(37)(29)(76)(26)(19)(41)(63)(76)(32)(33)(88)(41)(16)(25)(37)(73)(63)(92)(75)(120)(159)(55)(67)(13)(1)33949人阅读
昨天主管让我把VS项目里的warning给清理下,结果发现几个这样的warning:
warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode &#26684;式以防止数据丢失
刚用VS不久的我就在网上搜寻各种解决方案,结果都不好使,比如好多网友说:
(1)转换Code文件为Unicode&#26684;式;
(2)在Project&-&&Properties&-&&Configuration&Properties&-&&C/C&#43;&#43;&-&&Advance&的&Disable&Specific&Warnings&中添加相应的警告编号:4819;
(3)或找出不符合Unicode&#26684;式的文件,然后在该文件的开始处加入下面的语句:
&&&&&&&&#&pragma&warning&(disable:4819)
(4)哪个文件出现这个警告错误,打开它,&用VS2010的查找替换功能,打开允许正则表达式选项,选择当前窗口,查找替换&\n&为&\n&,&&然后,这个世界就清净了。原因:&查找的&\n&是跨平台的回车,替换的&\n&却是当前代码页的回车了。
(1)没有试过,(2)(3)也没试过,因为即便解决了warning,也是治标不治本,对于(4)我试了好多遍结果都是枉然~;
今天早上来到公司,继续寻找解决方案,经过其他网友提示,开始自己摸索着调试了,功夫不负有心人,最终问题被我解决了,下面是我的解决方案,给大家呈上(我用的是VS2010):
首先打开有该warning的文件,点击【文件】选【高级保存选项】,显示如下,或是其他别的编码&#26684;式,
现在开始改变编码&#26684;式为【简体中文(GB2312)- 代码页936】或【Unicode】,总之是适合自己的编码&#26684;式
点击【确定】后保存,重新编译,warning消失。
到这里相信问题已经解决,祝君好运!
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:48025次
排名:千里之外
评论:33条
(2)(1)(1)(1)(1)

我要回帖

更多关于 radialix 3 破解 的文章

 

随机推荐