我正在开发一款Python 3 Tkinter应用程序(操作系统为Windows 10),其功能概述如下:
1. 读取多个文本文件,这些文件可能包含ascii、cp1252、utf-8或其他任何编码的数据。
2. 在“预览窗口”(Tkinter标签小部件)中显示任何一个文件的内容。
3. 将文件内容写入单个输出文件(每次打开以追加方式)。
对于第1点:我通过在二进制模式下打开和读取文件使文件阅读编码无关。要将数据转换为字符串,我使用一个循环,遍历一个“可能性”编码列表,并依次尝试每个编码(使用
对于第2点:一旦我有了解码后的字符串,我只需调用Tkinter Label的
对于第3点:我以通常的方式打开一个输出文件,并使用
1. 读取多个文本文件,这些文件可能包含ascii、cp1252、utf-8或其他任何编码的数据。
2. 在“预览窗口”(Tkinter标签小部件)中显示任何一个文件的内容。
3. 将文件内容写入单个输出文件(每次打开以追加方式)。
对于第1点:我通过在二进制模式下打开和读取文件使文件阅读编码无关。要将数据转换为字符串,我使用一个循环,遍历一个“可能性”编码列表,并依次尝试每个编码(使用
error = 'strict'
),直到它找到一个不会抛出异常的编码。这一步骤已经实现。对于第2点:一旦我有了解码后的字符串,我只需调用Tkinter Label的
textvariable
的set()
方法即可。这一步也已经实现。对于第3点:我以通常的方式打开一个输出文件,并使用
write()
方法来写入已解码的字符串。当字符串解码为utf-8时,这种方法可以正常工作,但当它被解码为ascii或cp1252时,它会抛出一个异常:'charmap' codec can't encode characters in position 0-3: character maps to <undefined>
我搜索了一下,发现了一些相似的问题,但似乎没有解决我的问题。有一些进一步的限制使得一些解决方案对我无法起作用:
A. 我可以通过将读入的数据保留为字节并将输出文件打开/写入为二进制来规避这个问题,但这会导致一些输入文件内容无法读取。
B. 虽然这个应用程序主要是为Python 3设计的,但我正在尝试使其与Python 2兼容——我们有一些使用较慢/较晚版本的人会使用它。(顺便说一下,当我在Python 2上运行应用程序时,它也会抛出异常,但是对cp1252数据和utf-8数据都是如此。)
为了说明问题,我创建了这个简化的测试脚本。(我的真实应用程序是一个更大的项目,也是我的公司的专有项目,因此不会公开发布!)
import tkinter as tk
import codecs
#Root window
root = tk.Tk()
#Widgets
ctrlViewFile1 = tk.StringVar()
ctrlViewFile2 = tk.StringVar()
ctrlViewFile3 = tk.StringVar()
lblViewFile1 = tk.Label(root, relief=tk.SUNKEN,
justify=tk.LEFT, anchor=tk.NW,
width=10, height=3,
textvariable=ctrlViewFile1)
lblViewFile2 = tk.Label(root, relief=tk.SUNKEN,
justify=tk.LEFT, anchor=tk.NW,
width=10, height=3,
textvariable=ctrlViewFile2)
lblViewFile3 = tk.Label(root, relief=tk.SUNKEN,
justify=tk.LEFT, anchor=tk.NW,
width=10, height=3,
textvariable=ctrlViewFile3)
#Layout
lblViewFile1.grid(row=0,column=0,padx=5,pady=5)
lblViewFile2.grid(row=1,column=0,padx=5,pady=5)
lblViewFile3.grid(row=2,column=0,padx=5,pady=5)
#Bytes read from "files" (ascii Az5, cp1252 European letters/punctuation, utf-8 Mandarin characters)
inBytes1 = b'\x41\x7a\x35'
inBytes2 = b'\xe0\xbf\xf6'
inBytes3 = b'\xef\xbb\xbf\xe6\x9c\xa8\xe5\x85\xb0\xe8\xbe\x9e'
#Decode
outString1 = codecs.decode(inBytes1,'ascii','strict')
outString2 = codecs.decode(inBytes2,'cp1252','strict')
outString3 = codecs.decode(inBytes3,'utf_8','strict')
#Assign stringvars
ctrlViewFile1.set(outString1)
ctrlViewFile2.set(outString2)
ctrlViewFile3.set(outString3)
#Write output files
try:
with open('out1.txt','w') as outFile:
outFile.write(outString1)
except Exception as e:
print(inBytes1)
print(str(e))
try:
with open('out2.txt','w') as outFile:
outFile.write(outString2)
except Exception as e:
print(inBytes2)
print(str(e))
try:
with open('out3.txt','w') as outFile:
outFile.write(outString3)
except Exception as e:
print(inBytes3)
print(str(e))
#Start GUI
tk.mainloop()
调用替换为
io.open(..., 'w', encoding=...)`以实现Py2/3和跨平台兼容性。 - lenzio
模块就是解决这个问题的方法。正如下面Mark Tolonen所提到的,显式编码为UTF-8可解决write()
问题,而io
模块支持2和3的编码。可以将其作为“官方”答案,我会接受它的。 - JDM