问题分析
在 Python 中使用 PyExecJS 库运行 JavaScript 代码文件时,若文件中含有中文字符,则会被抛出一个文件编码异常 UnicodeEncodeError: 'gbk' codec can't encode character '\ufeff' in position 61: illegal multibyte sequence
。
异常详情如下:
Traceback (most recent call last):
File "D:/Projects/PythonProjects/ConfigurationTableVerificationTools/checkoutAllRoomFishCanKill.py", line 233, in <module>
game_table = execjs.get('local_node').compile(js_code).eval('_default')
File "D:\Projects\PythonProjects\ConfigurationTableVerificationTools\venv\lib\site-packages\execjs\_abstract_runtime_context.py", line 27, in eval
return self._eval(source)
File "D:\Projects\PythonProjects\ConfigurationTableVerificationTools\venv\lib\site-packages\execjs\_external_runtime.py", line 77, in _eval
return self.exec_(code)
File "D:\Projects\PythonProjects\ConfigurationTableVerificationTools\venv\lib\site-packages\execjs\_abstract_runtime_context.py", line 18, in exec_
return self._exec_(source)
File "D:\Projects\PythonProjects\ConfigurationTableVerificationTools\venv\lib\site-packages\execjs\_external_runtime.py", line 86, in _exec_
output = self._exec_with_pipe(source)
File "D:\Projects\PythonProjects\ConfigurationTableVerificationTools\venv\lib\site-packages\execjs\_external_runtime.py", line 102, in _exec_with_pipe
stdoutdata, stderrdata = p.communicate(input=input)
File "C:\Program Files\Python38\lib\subprocess.py", line 1028, in communicate
stdout, stderr = self._communicate(input, endtime, timeout)
File "C:\Program Files\Python38\lib\subprocess.py", line 1393, in _communicate
self._stdin_write(input)
File "C:\Program Files\Python38\lib\subprocess.py", line 962, in _stdin_write
self.stdin.write(input)
UnicodeEncodeError: 'gbk' codec can't encode character '\ufeff' in position 61: illegal multibyte sequence
解决方案
网络上其他文章的解决方案大多是直接修改 Python 标准库中 subprocess.py
模块中代码的方式,将其中编码格式设定为 UTF-8
。
但我认为,不到万不得已,能够在不修改官方标准模块文件,不对非项目代码文件进行侵入式的修改,就能够将问题解决的解决方案,才是更值得推荐的做法。
通过尝试,发现借助 Python 高度动态化的特性,在代码运行过程中,对 subprocess.py
模块中的 Popen
类进行重置及可解决该问题。具体代码如下。
import subprocess
# 创建一个新的 Popen 类,并继承自 subprocess.Popen
class MySubprocessPopen(subprocess.Popen):
def __init__(self, *args, **kwargs):
# 在调用父类(即 subprocess.Popen)的构造方法时,将 encoding 参数直接置为 UTF-8 编码格式
super().__init__(encoding='UTF-8', *args, **kwargs)
# 必须要在导入 PyExecJS 模块前,就将 subprocess.Popen 类重置为新的类
subprocess.Popen = MySubprocessPopen
# 导入 PyExecJS 模块
import execjs
# 在后面继续写程序具体的执行逻辑 ...