Python: subprocess.Popen()不支持unicode问题解决
起源:
所下载视频,有音视频分离者,需要合并起来,采用python之subprocess.Popen()调用ffmpeg实现。python版本为2.7.13,而音视频文件路径,有unicode字符者,合并失败。
此问题由来已久,终于不忍受,用尽工夫寻其机现,终于寻得蛛丝蚂迹,完成其修复。
其原因为:python 2.7.x中subprocess.Popen()函数,最终调用了kernel32.dll中的CreateProcess函数Ansi版本CreateProcessA,传非Ansi参数给它会被它拒绝,而触发异常。
测试代码如下:
# encoding: utf-8 from __future__ import unicode_literals
import subprocess file_path = r'D:\Percy Faith [CA US] by chkjns ♫ 175 songs\v.txt'
args = u'notepad "%s"' % file_path try:
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
if p.returncode != 0:
stderr = stderr.decode('utf-8', 'replace')
print stderr
except Exception as e:
print e.message
其异常为UnicodeEncodeError,表现字串为:'ascii' codec can't encode character u'\u266b' in position 42: ordinal not in range(128)
1、Python 2.x先天缺陷
此问题百度不到有效答案,于是上stackoverlow,一路摸去,找到了python官方对此bug描述,其链接如下:
Issue 19264: subprocess.Popen doesn't support unicode on Windows - Python tracker
七嘴八舌众说纷纭,但总算大致捋清原委,即上述其调用了Ansi版本的CreateProcess所致。循此原因,是否替换为Unicode函数CreateProcessW即可?
最后一条回复:
msg289664 - (view) Author: Valentin LAB (Valentin LAB) Date: 2017-03-15 10:39
给出了折中方案,其方案即如所思,做函数层替换。
2、win_subprocess.py
Valentin LAB这哥们,就在github上开放了源码,解决此问题。其核心为win_subprocess.py,内容如下:
(其代码中有用os,他却忘记import,贴代码时已做修正)
## issue: https://bugs.python.org/issue19264 import ctypes
import subprocess
import _subprocess
import os
from ctypes import byref, windll, c_char_p, c_wchar_p, c_void_p, \
Structure, sizeof, c_wchar, WinError
from ctypes.wintypes import BYTE, WORD, LPWSTR, BOOL, DWORD, LPVOID, \
HANDLE ##
## Types
## CREATE_UNICODE_ENVIRONMENT = 0x00000400
LPCTSTR = c_char_p
LPTSTR = c_wchar_p
LPSECURITY_ATTRIBUTES = c_void_p
LPBYTE = ctypes.POINTER(BYTE) class STARTUPINFOW(Structure):
_fields_ = [
("cb", DWORD), ("lpReserved", LPWSTR),
("lpDesktop", LPWSTR), ("lpTitle", LPWSTR),
("dwX", DWORD), ("dwY", DWORD),
("dwXSize", DWORD), ("dwYSize", DWORD),
("dwXCountChars", DWORD), ("dwYCountChars", DWORD),
("dwFillAtrribute", DWORD), ("dwFlags", DWORD),
("wShowWindow", WORD), ("cbReserved2", WORD),
("lpReserved2", LPBYTE), ("hStdInput", HANDLE),
("hStdOutput", HANDLE), ("hStdError", HANDLE),
] LPSTARTUPINFOW = ctypes.POINTER(STARTUPINFOW) class PROCESS_INFORMATION(Structure):
_fields_ = [
("hProcess", HANDLE), ("hThread", HANDLE),
("dwProcessId", DWORD), ("dwThreadId", DWORD),
] LPPROCESS_INFORMATION = ctypes.POINTER(PROCESS_INFORMATION) class DUMMY_HANDLE(ctypes.c_void_p): def __init__(self, *a, **kw):
super(DUMMY_HANDLE, self).__init__(*a, **kw)
self.closed = False def Close(self):
if not self.closed:
windll.kernel32.CloseHandle(self)
self.closed = True def __int__(self):
return self.value CreateProcessW = windll.kernel32.CreateProcessW
CreateProcessW.argtypes = [
LPCTSTR, LPTSTR, LPSECURITY_ATTRIBUTES,
LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCTSTR,
LPSTARTUPINFOW, LPPROCESS_INFORMATION,
]
CreateProcessW.restype = BOOL ##
## Patched functions/classes
## def CreateProcess(executable, args, _p_attr, _t_attr,
inherit_handles, creation_flags, env, cwd,
startup_info):
"""Create a process supporting unicode executable and args for win32 Python implementation of CreateProcess using CreateProcessW for Win32 """ si = STARTUPINFOW(
dwFlags=startup_info.dwFlags,
wShowWindow=startup_info.wShowWindow,
cb=sizeof(STARTUPINFOW),
## XXXvlab: not sure of the casting here to ints.
hStdInput=int(startup_info.hStdInput),
hStdOutput=int(startup_info.hStdOutput),
hStdError=int(startup_info.hStdError),
) wenv = None
if env is not None:
## LPCWSTR seems to be c_wchar_p, so let's say CWSTR is c_wchar
env = (unicode("").join([
unicode("%s=%s\0") % (k, v)
for k, v in env.items()])) + unicode("\0")
wenv = (c_wchar * len(env))()
wenv.value = env pi = PROCESS_INFORMATION()
creation_flags |= CREATE_UNICODE_ENVIRONMENT if CreateProcessW(executable, args, None, None,
inherit_handles, creation_flags,
wenv, cwd, byref(si), byref(pi)):
return (DUMMY_HANDLE(pi.hProcess), DUMMY_HANDLE(pi.hThread),
pi.dwProcessId, pi.dwThreadId)
raise WinError() class Popen(subprocess.Popen):
"""This superseeds Popen and corrects a bug in cPython 2.7 implem""" def _execute_child(self, args, executable, preexec_fn, close_fds,
cwd, env, universal_newlines,
startupinfo, creationflags, shell, to_close,
p2cread, p2cwrite,
c2pread, c2pwrite,
errread, errwrite):
"""Code from part of _execute_child from Python 2.7 (9fbb65e) There are only 2 little changes concerning the construction of
the the final string in shell mode: we preempt the creation of
the command string when shell is True, because original function
will try to encode unicode args which we want to avoid to be able to
sending it as-is to ``CreateProcess``. """
if not isinstance(args, subprocess.types.StringTypes):
args = subprocess.list2cmdline(args) if startupinfo is None:
startupinfo = subprocess.STARTUPINFO()
if shell:
startupinfo.dwFlags |= _subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = _subprocess.SW_HIDE
comspec = os.environ.get("COMSPEC", unicode("cmd.exe"))
args = unicode('{} /c "{}"').format(comspec, args)
if (_subprocess.GetVersion() >= 0x80000000 or
os.path.basename(comspec).lower() == "command.com"):
w9xpopen = self._find_w9xpopen()
args = unicode('"%s" %s') % (w9xpopen, args)
creationflags |= _subprocess.CREATE_NEW_CONSOLE super(Popen, self)._execute_child(args, executable,
preexec_fn, close_fds, cwd, env, universal_newlines,
startupinfo, creationflags, False, to_close, p2cread,
p2cwrite, c2pread, c2pwrite, errread, errwrite) _subprocess.CreateProcess = CreateProcess
3、使用方法
若已在.py文件中引入unicode标记(建议自有项目,皆加以unicode支持):
from __future__ import unicode_literals
那么,直接import win_subprocess就行,其代码中,已以自定义CreateProcess替换了_subprocess.CreateProcess同名函数。
当然,也可直接以win_subprocess.Popen()调用。
但是,经过验证,直接引用win_subprocess就能很好工作了,因此推荐直接引过去就行。
参考资料:
Fixing python 2.7 windows unicode issue with 'subprocess.Popen'
Python: subprocess.Popen()不支持unicode问题解决的更多相关文章
- python subprocess popen 静默模式(不弹出console控制台)
python subprocess popen 静默模式(不弹出console控制台) import subprocess,sys IS_WIN32 = 'win32' in str(sys.plat ...
- Python subprocess.Popen communicate() 和wait()使用上的区别
之所以会纠结到这个问题上是因为发现在调用Popen的wait方法之后程序一直没有返回.google发现wait是有可能产生死锁的.为了把这个问题彻底弄清楚,搜索一些资料过来看看: 原文链接:http: ...
- Python Subprocess Popen 管道阻塞问题分析解决
http://ju.outofmemory.cn/entry/279026 场景:1>不断播放mp3文件: 2>使用订阅发布模式保持tcp长连接,从服务器接收信息 造成程序hang死,但是 ...
- Python subprocess.Popen中communicate()和wait()区别
刚开始我是使用的wait(),但是当adb命令返回太多时,程序就会卡死,查询得知原因后,才使用了communicate(),communicate()返回一个元组:(stdoutdata, stder ...
- python subprocess.Popen 非阻塞
1.非阻塞设置subprocess.Popen(args, stdout=subprocess.PIPE,stderr=subprocess.PIPE) def non_block_read(outp ...
- Python subprocess Popen
目的:顺序执行进程 在Bash里面类似 a.sh && b.sh && c.sh 先来说下Popen这个函数 class subprocess.Popen(args ...
- python subprocess.Popen 控制台输出 实时监控百度网ping值
import subprocess file_out = subprocess.Popen('ping www.baidu.com', shell=True, stdout=subprocess.PI ...
- Python subprocess.Popen() error (No such file or directory)
这个错误很容易引起误解,一般人都会认为是命令执行了,但是命令找不到作为参数对应的文件或者目录.其实还有一层含义,就是这个命令找不到,命令找不到,也会报没有这个文件或者目录的错误. 为什么找不到这个命令 ...
- python中的subprocess.Popen()使用
python中的subprocess.Popen()使用 从python2.4版本开始,可以用subprocess这个模块来产生子进程,并连接到子进程的标准输入/输出/错误中去,还可以得到子进程的返回 ...
随机推荐
- mysql每天凌晨0点准时启动taskeng.exe如何关闭
MySQL弹出一个taskeng.exe. 内容如下:=====================Start Initialization====================mysql Instal ...
- NSMapTable
跟NSDictionary用法差不多,不过区别是NSMapTable可以设置内存选项,例如可以设置key跟value的内存属性(weak/strong),从而避免内存泄露. 例如这个+ weakToW ...
- hadoop-1
结合其他文章 http://weixiaolu.iteye.com/blog/1504898 https://www.cnblogs.com/dycg/p/3934394.html https://b ...
- leetcode AC1 感受
在网上第一个AC还是蛮高兴的,之前试了不少练习编程的方法,感觉不怎么适合自己,在OJ上做题的确是一个能够锻炼的方法. 之前一直研究学习的方法,往简单的说是认知.练习,往复杂的说是,保持足够input, ...
- 利用目录函数(opendir,readdir,closedir)查找文件个数
如何知道一个目录下的所有文件个数呢?或许可以用tree来学(zhuang)习(bi)的同时知道文件个数.Linux系统io函数为我们提供了目录操作函数,其中有一个比较重要(实际上有三个,因为它们经常配 ...
- vue项目bug-Couldn’t find preset "es2015"
在使用vue项目的时候安装了其他的插件,发现会报错 Couldn’t find preset "es2015".是因引用的插件使用了es标准,解决办法如下 npm install ...
- GPS坐标转换 百度地图API调用
1 如果GPS输出的值是DD.DDDDDDDD格式的,直接调用地图API的转换函数处理,就可以正常显示2 如果GPS输出的值是DD.MMMMMMMM格式的,就需要先进行分转度处理,然后再调API,就可 ...
- 6.面向对象 -类.md
目录 1. static: 2. 类在内存中,每一个类在创建在栈内存中,当创建一个对象的时候,将非类变量再堆内存中创建,而类变量是不会因为创建对象而在堆中重新创建 3. 对象.引用和指针: 4. 类名 ...
- 历届试题 对局匹配-(dp)
问题描述 小明喜欢在一个围棋网站上找别人在线对弈.这个网站上所有注册用户都有一个积分,代表他的围棋水平. 小明发现网站的自动对局系统在匹配对手时,只会将积分差恰好是K的两名用户匹配在一起.如果两人分差 ...
- 学习-HTML5
@@ 学习HTML5发现对我们开发工作者来说要方便很多,它现在还在发展阶段,在未来肯定会是主流. 我们知道HTML5目的是取代HTML4.01和XHTML1.0标准,他希望能够减少互联网富应用(RIA ...