[英]Unicode filenames on Windows with Python & subprocess.Popen()
為什么會發生以下情況:
>>> u'\u0308'.encode('mbcs') #UMLAUT
'\xa8'
>>> u'\u041A'.encode('mbcs') #CYRILLIC CAPITAL LETTER KA
'?'
>>>
我有一個Python應用程序,它接受來自操作系統的文件名。 它適用於某些國際用戶,但不適用於其他用戶。
例如,此unicode文件名:u'\\ u041a \\ u0433 \\ u044b \\ u044b \\ u0448 \\ u0444 \\ u0442'
不會使用Windows的“ mbcs”編碼(文件系統使用的一種,由sys.getfilesystemencoding()返回)進行編碼。 我得到'???????',表明編碼器在這些字符上失敗。 但這沒有任何意義,因為文件名始於用戶。
更新:這是我背后原因的背景...我的系統上有一個文件名為Cyrillic。 我想用該文件作為參數調用subprocess.Popen()。 Popen無法處理unicode。 通常,我可以使用sys.getfilesystemencoding()給定的編解碼器對參數進行編碼。 在這種情況下,它將無法正常工作
在Py3K中-至少從Python 3.2起subprocess.Popen
和sys.argv
在Windows上與(默認unicode)字符串一致地工作。 顯然使用了CreateProcessW
和GetCommandLineW
。
在Python -高達V2.7.2至少- subprocess.Popen
是車用Unicode參數。 它堅持使用CreateProcessA
(而os.*
與Unicode一致)。 shlex.split
會創建其他廢話。
Pywin32的win32process.CreateProcess
也不會自動切換到W版本,也沒有win32process.CreateProcessW
。 與GetCommandLine
相同。 因此ctypes.windll.kernel32.CreateProcessW...
需要使用ctypes.windll.kernel32.CreateProcessW...
子過程模塊也許應該就此問題進行修復。
帶有私人應用程序的argv[1:]
上的UTF8在Unicode操作系統上仍然很笨拙。 這些技巧對於像Linux這樣的8位“ Latin1”字符串操作系統可能是合法的。
UPDATE vaab創造了一個修補版本Popen
為Python 2.7,修正了問題。
參見https://gist.github.com/vaab/2ad7051fc193167f15f85ef573e54eb9
博客文章及其說明: http : //vaab.blog.kal.fr/2017/03/16/fixing-windows-python-2-7-unicode-issue-with-subprocesss-popen/
免責聲明:我是以下提到的修復程序的作者。
要在帶有python 2.7的Windows上支持unicode命令行,可以使用此補丁程序進行subprocess.Popen(..)
情況
Windows上的unicode命令行的Python 2支持非常差。
嚴重錯誤:
從調用方向系統發出unicode命令行(通過subprocess.Popen(..)
),
並從被調用方(通過sys.argv
)讀取當前命令行的unicode參數,
它是公認的, 不會在Python 2上修復。這些已在Python 3中修復。
技術原因
在Python 2中, subprocess.Popen(..)
和sys.argv
Windows實現使用非Unicode就緒的Windows系統調用CreateProcess(..)
(請參閱python 代碼和CreateProcess的 MSDN 文檔 ),而不使用GetCommandLineW(..)
的sys.argv
在Python 3中, subprocess.Popen(..)
Windows實現使用正確的Windows系統,該系統從3.0
開始(請參閱3.0
代碼 CreateProcessW(..)
調用CreateProcessW(..)
,而sys.argv
使用從3.3
開始的GetCommandLineW(..)
(請參閱3.3
代碼 )。
它如何固定
給定的補丁將利用ctypes
模塊直接調用C Windows系統CreateProcessW(..)
。 它通過重寫私有方法Popen._execute_child(..)
和私有函數_subprocess.CreateProcess(..)
來提議一個新的固定Popen
對象,以盡可能模仿Windows系統lib的方式來設置和使用CreateProcessW(..)
如何在Python 3.6
完成。
如何使用它
sys.getfilesystemencoding()的文檔說,對於Windows NT和更高版本,文件名本地為Unicode。 如果您有一個有效的unicode文件名,為什么還要麻煩使用mbcs對其進行編碼?
編解碼器文檔模塊說mbcs使用“ ANSI代碼頁”進行編碼(具體取決於用戶的語言環境),因此,如果語言環境不使用西里爾字母,請使用splat。
編輯:所以您的進程正在調用subprocess.Popen()。 如果您調用的進程在您的控制之下,則這兩個進程應該能夠同意使用UTF-8作為Unicode傳輸格式。 否則,您可能需要在pywin32郵件列表中詢問。 無論如何,請編輯問題以說明您對調用的過程的控制程度。
如果需要傳遞現有文件的名稱,則可以通過傳遞8.3版本的Unicode文件名來獲得更大的成功機會。
您需要安裝pywin32軟件包,然后可以執行以下操作:
>>> import win32api
>>> win32api.GetShortPathName(u"C:\\Program Files")
'C:\\PROGRA~1'
我相信這些短文件名僅使用ASCII字符,因此您應該能夠將它們用作命令行的參數。
如果還需要指定要創建的文件名,則可以使用Unicode文件名從Python預先以零大小創建它們,並將文件的簡稱作為參數傳遞。
更新:用戶bogdan正確地說可以禁用8.3文件名生成(當我在筆記本電腦上安裝Windows XP時也禁用了它),因此您不能依賴它們。 因此,作為在NTFS卷上工作時另一種牽強的方法,可以將Unicode文件名硬鏈接到純ASCII文件名。 將ASCII文件名傳遞給外部命令,然后將其刪除。
使用Python 3時,請不要對字符串進行編碼。 Windows文件名是本機Unicode,Python 3中的所有字符串都是Unicode,Popen使用CreateProcess
Windows API函數的Unicode版本。
對於Python 2.7,最簡單的解決方案是使用第三方模塊https://pypi.org/project/subprocessww/ 。 沒有獲得完整Unicode支持(獨立於系統區域設置)的“內置”解決方案,Python 2.7的維護者認為這是功能請求,而不是錯誤修正,因此這不會改變。
有關為何事物保持原狀的詳細技術說明,請參閱其他答案。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.