簡體   English   中英

在Windows上使用python和subprocess.Popen()的Unicode文件名

[英]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.Popensys.argv在Windows上與(默認unicode)字符串一致地工作。 顯然使用了CreateProcessWGetCommandLineW

在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.argv

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM