从tk对话框返回的UTF8文件名的UnicodeEncodeError

2024-09-30 18:29:55 发布

您现在位置:Python中文网/ 问答频道 /正文

嗨,我正在尝试使用带有以下函数的ffmpeg从视频文件中提取音频(在Python 2中):

def extractAudio(path):
    command = ''.join(('ffmpeg -i "',path,'" -ab 160k -ac 2 -ar 44100 -vn audio.wav'))
    print(command)
    subprocess.call(command,shell=True)

上面的print语句成功地打印了以下内容:

^{pr2}$

但在下一个语句中,它失败并引发以下错误:

Traceback (most recent call last):
  File "C:/Users/pruthvi/Desktop/vidrec/vidrec.py", line 53, in <module>
    main()
  File "C:/Users/pruthvi/Desktop/vidrec/vidrec.py", line 46, in main
    extractAudio(os.path.join(di,each))
  File "C:/Users/pruthvi/Desktop/vidrec/vidrec.py", line 28, in extractAudio
    subprocess.call(command,shell=True)
  File "C:\Python27\lib\subprocess.py", line 522, in call
    return Popen(*popenargs, **kwargs).wait()
  File "C:\Python27\lib\subprocess.py", line 710, in __init__
    errread, errwrite)
  File "C:\Python27\lib\subprocess.py", line 928, in _execute_child
    args = '{} /c "{}"'.format (comspec, args)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 56-57: ordinal not in range(128)

我已经尝试了前面问题中所有可能的解决方案,比如用正确的类型编码,设置PYTHONIOENCODING等等,但是似乎没有一个有效。如果我将其转换为ascii,它将不再起作用,因为它删除了非ascii字符并以file not found结尾,并且音频不会从目标文件中提取。感谢任何帮助,谢谢:)

要进行实验,可以使用以下代码:

# -*- coding: utf-8 -*-
import subprocess

def extractAudio():
    path = u'C:/Users/pruthvi/Desktop/vidrec/temp\TAEYEON 태연_ I (feat. Verbal Jint)_Music Video.mp4'
    command = ''.join(('ffmpeg -i "',path,'" -ab 160k -ac 2 -ar 44100 -vn audio.wav'))
    print(command)
    subprocess.call(command,shell=True)
extractAudio()

Tags: pathinpylinecallusersffmpegcommand
3条回答

你有两个问题:

Python2

在任何参数中使用Unicode时,subprocess模块将中断。这个问题在python3中得到了修复,您可以将任何Unicode文件名和参数传递给subprocess,它将正确地将这些文件名和参数转发给子进程。在

ffmpeg

ffmpeg本身无法打开这些文件,您只需尝试从命令行运行即可轻松验证这些文件:

C:\temp>fancy αβγ.m4v
... lots of other output
fancy a�?.m4v: Invalid data found when processing input

(我的代码页是windows-1252,请注意希腊语α是如何被拉丁语a取代的)

你不能解决这个问题,但你可以解决它,见bobince的答案。在

因为您正在向subprocess.call传递一个unicode字符串,Python尝试将其编码为它认为文件系统/操作系统能够理解的编码。因为某些原因,它选择了ASCII,这是错误的。在

您可以通过

subprocess.call(command.encode(sys.getfilesystemencoding()))

与您上一个问题相同:大多数Windows上的跨平台软件无法处理文件名中的非ASCII字符。在

Python的subprocess模块使用基于字节字符串的接口。在Windows下,命令行基于Unicode字符串(技术上是UTF-16代码单元),因此msc运行时使用编码(ANSI代码页)将字节字符串转换为Unicode字符串,这种编码方式因机器而异,永远不能包含所有Unicode字符。在

如果您的Windows安装是朝鲜语,则ANSI代码页将是949朝鲜语,您可以通过说出以下命令之一来编写命令:

subprocess.call(command.encode('cp949'))
subprocess.call(command.encode('mbcs'))

(其中mbcs是“多字节字符集”的缩写,它是Windows上ANSI代码页的同义词。)如果您的安装不是朝鲜语,您将有一个不同的ANSI代码页,您将无法将该文件名写入命令,因为您的命令行编码中没有任何朝鲜文字母。ANSI编码从来没有像UTF-8那样有意义,因此没有人可以可靠地使用子进程来执行包含所有Unicode字符的命令。在

如前一个问题中所讨论的,Python为Unicode文件名提供了一些解决方法,可以使用本机win32api而不是C标准库。在python3中,它还使用win32unicode api来创建进程,但在python2中就不是这样了。通过ctypes调用Win32CreateProcessW命令,可以直接访问Windows api,但这有点麻烦。在

…而且无论如何也没有用,因为即使您在命令行中输入了非ANSI字符,ffmpeg命令本身也会失败。这是因为ffmpeg也是一个跨平台的应用程序,它使用C标准库来读取命令行和文件。它将无法读取命令行参数中的朝鲜语,即使您以某种方式完成了它也无法读取该名称的文件!在

这是Windows平台上持续不断的挫败感:尽管它内部支持Unicode非常好,但大多数运行在它上面的工具却不能。答案应该是Windows在它实现的所有字节字符串接口中都支持UTF-8,而不是没有人想要的糟糕的旧版ANSI代码页。不幸的是,微软一再拒绝采取任何第一步措施,使UTF-8成为Windows上的一流公民(即修复一些阻止UTF-8在控制台中工作的错误)。对不起的。在

无关:这个:

^{pr2}$

通常是个坏主意。文件名中有许多特殊字符会破坏命令行,并可能最终执行所有类型的其他命令。如果输入路径由不可信的人控制,这将是一个严重的安全漏洞。一般来说,当你把一个来自变量的命令行放在一起时,你需要应用转义来保证字符串的安全性,而Windows上的转义规则又复杂又烦人。在

通过将所有内容保留在Python中,可以避免转义问题和Unicode问题。不必启动命令来调用ffmpeg代码,您可以使用一个将ffmpeg的功能引入Python的模块,例如PyFFmpeg。在

或者一个廉价的解决方法是将文件复制/移动到Python中已知的安全名称,使用静态文件名运行ffmpeg命令,然后重命名/复制该文件。。。在

相关问题 更多 >