快速查看解决办法请直接拖到文末第四部分
一、发现问题
最近在使用psychopy3时出现一件非常奇怪的事情:运行结束后保存的csv文件中文乱码并错行。
如图所示:instr是指导语变量,image_file是图片的路径。
二、实验背景描述
这是一个很简单的使用程序,我就直接用了psychopy图形化的builder窗口。这个实验用conditions.xlsx文件控制循环。因为不同的trial指导语不一样,在conditions里面写了一些中文的指导语。但在运行完该程序后,获取到的数据里面中文全部乱码而且有错列,例如本来应该两列的东西拼到了一列。但是在运行呈现的时候,展现出来的指导语都是中文,且正确。
结合windows下使用python的经验,我估摸着是windows的编码问题。
三、问题排除逻辑
3.1 先要确认是不是写文件的时候编码有误。
1、首先找到psychopy运行的lastrun.py代码,发现结尾部分的saveAsWideText函数定义了最终的文件存储。
2、接着找到saveAsWideText函数定义的地方,根据psychopy官网的Reference Manul (API)发现是在experiment Handler 里面定义了该函数,在github上也确认了这点。回到我的电脑,就 是在psychopy安装路径下的data\experiment.py文件里。我这电脑里是:D:\Program Files (x86)\PsychoPy3\Lib\site-packages\psychopy\data\experiment.py
3、找到249行 def saveAsWideText的地方,那里传入了encoding = 'utf-8'的参数,看上去没有问题。整个函数也很清晰,在写入文件的时候,作者是有提前考虑到编码问题了。
3.2 写文件的编码设为utf-8是正确的,是否打开conditions.xlsx文件时没有传入编码?
4、发现打开文件是在305行的openOutputFile函数里使用的,而该函数是从psychopy.tools.filetools里面导入的。于是查看filetools模块。找到94行发现OutputFile这个函数在传入参数的时候也默认了'utf-8',所以这个地方应该也没问题。
def openOutputFile(fileName=None, append=False, fileCollisionMethod='rename',
encoding='utf-8'):
5、【顿悟】突然联想到之前在这个电脑上用python保存文件的时候,用utf-8不行,得用utf-8-sig,所以我把第3步saveAsWideText的encoding改成了'utf-8-sig'。再运行就没有乱码了(见下图)。
到这里基本上已经解决了主要问题。但是后续还会出现这种情况,需要进一步查明为什么utf-8在这里无效。首先一个个排除了.py文件的编码问题,全是utf-8。
3.3 接下来猜想可能是conditions.xlsx文件的编码问题,这玩意就不是utf-8的编码?新建了conditions.csv文件,在encoding = 'utf-8'的时候也这样,看来是别的原因了。
6、在python官网找到关于编码问题的解释,里面写到:"为了可靠的探测编码……微软为它的记事本程序发明了utf-8 with BOM,也就是(python里的utf-8-sig),特点是会在所有Unicode之前加上0xef, 0xbb, 0xbf……在python里面要避免这玩意儿。" 这可能就是windows系统的毛病了……
Without external information it’s impossible to reliably determine which encoding was used for encoding a string. Each charmap encoding can decode any random byte sequence. However that’s not possible with UTF-8, as UTF-8 byte sequences have a structure that doesn’t allow arbitrary byte sequences. To increase the reliability with which a UTF-8 encoding can be detected, Microsoft invented a variant of UTF-8 (that Python 2.5 calls "utf-8-sig") for its Notepad program: Before any of the Unicode characters is written to the file, a UTF-8 encoded BOM (which looks like this as a byte sequence: 0xef, 0xbb, 0xbf) is written. As it’s rather improbable that any charmap encoded file starts with these byte values (which would e.g. map to
LATIN SMALL LETTER I WITH DIAERESIS
RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
INVERTED QUESTION MARK
in iso-8859-1), this increases the probability that a utf-8-sig encoding can be correctly guessed from the byte sequence. So here the BOM is not used to be able to determine the byte order used for generating the byte sequence, but as a signature that helps in guessing the encoding. On encoding the utf-8-sig codec will write 0xef, 0xbb, 0xbf as the first three bytes to the file. On decoding utf-8-sig will skip those three bytes if they appear as the first three bytes in the file. In UTF-8, the use of the BOM is discouraged and should generally be avoided.
7、github提交问题反馈给psychopy项目的成员
我在Psychopy项目里提了个issue描述了遇到的这个问题,项目组的成员@hoechenberger 告诉我,可能是因为使用EXCEL打开CSV文件的问题,建议我试试LibreOffice这个软件打开(见下图)。我在安装并修改csv的默认打开程序为LibreOffice后,这个问题就解决了!用LibreOffice打开这个csv文件是没问题的。终于找到了问题所在!!!
四、解决办法小结
解决办法1:
1、找到psychopy安装路径下的experiment文件,例如:PsychoPy3\Lib\site-packages\psychopy\data\experiment.py
2、把里面def saveAsWideText这一行的 encoding = 'utf-8' 改成 encoding = 'utf-8-sig',保存!!
3、再运行就OK了。
这种方法有个弊端,就是如果你不使用Excel而是用R语言处理最终的csv数据,那么在用R读入该文件的时候可能需要指定encoding = "utf-8-sig"。
解决办法2:
安装LibreOffice,并将之修改为.csv文件的默认打开程序,该问题就解决了。弊端:这个办法就是要新安装一个软件。
PS:顺便安利一下这个LibreOffice,最开始是在linux上使用的开源办公软件,相当于office之于windows。后来这个开源项目不断壮大,现在LibreOffice已经有中文版了,如果厌倦了每年都要破解office套件的同学可以试试这款软件。
五、根本原因
最终发现根本问题在于使用windows里EXCEL打开这个csv文件时,EXCEL默认给这个文件加了BOM,导致这个原本UTF-8编码的文件显示错乱。
也就是原本UTF-8编码的文件,EXCEL非要用UTF-8-sig的编码去打开,导致显示错乱。