UnicodeEncodeError: 'ascii'编解码器无法在print函数中编码字符

4

我的公司正在使用数据库,我正在编写与该数据库交互的脚本。已经有一个脚本用于将查询放在数据库上,并且基于查询,该脚本将从数据库返回结果。

我正在unix环境下工作,并且在我的脚本中使用该脚本以从数据库获取一些数据,并将查询结果重定向到文件中。现在当我尝试读取这个文件时,我收到一个错误信息-

UnicodeEncodeError: 'ascii' codec can't encode character '\u2013' in position 9741: ordinal not in range(128)

我知道Python无法读取文件是因为文件的编码不是ASCII,所以会出现错误。我尝试检查文件的编码并使用其自己的编码来读取文件。
我正在使用的代码是 -
 os.system("Query.pl \"select title from bug where (ste='KGF-A' AND ( status = 'Not_Approved')) \">patchlet.txt")
 encoding_dict3={}
 encoding_dict3=chardet.detect(open("patchlet.txt", "rb").read())
 print(encoding_dict3)
# Open the patchlet.txt file for storing the last part of titles for latest ACF in a list
 with codecs.open("patchlet.txt",encoding='{}'.format(encoding_dict3['encoding'])) as csvFile
readCSV = csv.reader(csvFile,delimiter=":")
    for row in readCSV:
        if len(row)!=0:
            if len(row) > 1:
                j=len(row)-1
                patchlets_in_latest.append(row[j])
            elif len(row) ==1:
                patchlets_in_latest.append(row[0])               
patchlets_in_latest_list=[]
# calling the strip_list_noempty function for removing newline and whitespace characters
patchlets_in_latest_list=strip_list_noempty(patchlets_in_latest)
# coverting list of titles in set to remove any duplicate entry if present
patchlets_in_latest_set= set(patchlets_in_latest_list)
# Finding duplicate entries in  list
duplicates_in_latest=[k for k,v in Counter(patchlets_in_latest_list).items() if v>1]
# Printing imp info for logs
    print("list of titles of patchlets in latest list are : ")
for i in patchlets_in_latest_list:
   **print(str(i))**
print("No of patchlets in latest list are : {}".format(str(len(patchlets_in_latest_list))))

Query.pl是一个Perl脚本,用于从数据库中获取查询结果。我得到的“patchlet.txt”文件(用于存储来自HSD的结果)的编码方式是:

{'encoding': 'Windows-1252', 'confidence': 0.73, 'language': ''}

即使我提供了相同的编码来读取文件,我仍然会收到错误信息。
请帮助我解决这个错误。
编辑:我正在使用python3.6
编辑2:
在输出结果时,我遇到了错误,文件中有一行含有一些未知字符。该行如下所示:
“由于某些原因,vtrace不能与某些跟踪一起使用而导致失败。”
我正在使用gvim,在gvim中,“vtrace”看起来像“〜Vvtrace”。然后我手动检查了数据库中的这个字符,发现它是“-”,根据我的键盘,它既不是连字符也不是下划线。这些字符导致了问题。
另外,我正在Linux环境下工作。
编辑3:
我添加了更多的代码来帮助跟踪错误。我还突出显示了一个“print”语句(print(str(i))),在这里我遇到了错误。

1
你能分享一个 [mcve] 并告诉我们你正在运行哪个版本的 Python 吗?还有一个 traceback 吗? - snakecharmerb
2
我猜想问题出现在输出结果时,而不是读取输入时。但是如果没有一些能够重现问题的代码和数据,或者至少有代码和回溯信息,那么我们无法做更多的猜测。 - snakecharmerb
我已经添加了所有可能的信息。现在请查看问题-snakecharmerb - rikki
1
再次强调,回溯信息来自于您未展示的代码。在 with codecs.open 之后的某个地方,可能有一些尝试简单地向 Python 无法确定正确编码的位置进行 print 的操作。 - tripleee
@rikki,当你发布有关异常的问题时,请始终发布完整的回溯(即您在运行您发布的确切代码时获得的回溯,因此行号等匹配)-如果您至少不知道错误发生在哪里,则错误消息本身是无用的。 - bruno desthuilliers
显示剩余8条评论
1个回答

6

问题

根据问题中提供的信息,程序正在处理非ASCII字符数据,但无法输出非ASCII字符数据。

具体来说,是这段代码:

for i in patchlets_in_latest_list:
   print(str(i))

这个异常的结果为:

UnicodeEncodeError: 'ascii'编解码器无法编码字符'\u2013'

在Python 2中,将unicode对象调用str会导致Python尝试将对象编码为ASCII,如果对象包含非ASCII字符,则会导致UnicodeEncodeError

在Python 3中,在str实例上调用str不会触发任何编码。但是,在str上调用print函数会将str编码为sys.stdout.encodingsys.stdout.encoding默认为由locale.getpreferredencoding返回的值。 这通常是您Linux用户的LANG环境变量。

解决方法

如果我们假设您的程序未覆盖正常的编码行为,则可以通过确保在UTF-8区域设置中使用Python 3解释器来解决该问题。

  • 确信代码正在由Python 3解释器执行--从程序内部打印sys.version_info
  • 尝试在运行脚本时设置PYTHONIOENCODING环境变量:PYTHONIOENCODING=UTF-8 python3 myscript.py
  • 使用终端中的locale命令(或echo $LANG)检查您的区域设置。如果它没有以UTF-8结尾,请考虑更改它。如果您在企业机器上,请咨询系统管理员。
  • 如果您的代码在cron作业中运行,请记住cron作业通常使用'C'或'POSIX'语言环境--这可能使用ASCII编码--除非明确设置语言环境。同样,如果脚本在其他用户下运行,请检查其区域设置。

解决办法

如果更改环境不可行,您可以通过使用错误处理程序将其编码为ASCII,然后再解码回str来解决Python中的问题。

在您特定的情况下有四个有用的错误处理程序,它们的效果通过以下代码演示:

>>> s = 'Hello \u2013 World'
>>> s
'Hello – World'
>>> handlers = ['ignore', 'replace', 'xmlcharrefreplace', 'namereplace']
>>> print(str(s))
HelloWorld
>>> for h in handlers:
...     print(f'Handler: {h}:', s.encode('ascii', errors=h).decode('ascii'))
... 
Handler: ignore: Hello  World
Handler: replace: Hello ? World
Handler: xmlcharrefreplace: Hello – World
Handler: namereplace: Hello \N{EN DASH} World
< p > ignorereplace 处理程序会丢失信息 - 你无法知道哪个字符已被替换为空格或问号。

xmlcharrefreplacenamereplace 处理程序不会丢失信息,但替换序列可能会使文本对人类更难阅读。

对于程序输出的使用者来说,应该由您决定可接受的权衡。

如果您决定使用 replace 处理程序,则需要更改代码如下:

for i in patchlets_in_latest_list:
    replaced = i.encode('ascii', errors='replace').decode('ascii')
    print(replaced)

当您打印数据可能包含非ASCII字符时,需要进行相应的处理。


嗨......非常感谢您提供如此精彩的解释性答案。它完全解决了这个问题......非常感谢 - @snakecharmerb - rikki
谢谢!!!这篇文章的更多关键词有:Docker Python Ubuntu,“print()在iPython中不能正常工作”。 - Tony Fraser

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