Python argparse unicode 参数问题
Posted
技术标签:
【中文标题】Python argparse unicode 参数问题【英文标题】:Python argparse unicode argument issue 【发布时间】:2014-08-24 12:39:23 【问题描述】:我正在使用 python 的 argparse 模块来处理命令行参数。我在解码实际的 unicode 文件名/文件路径时遇到问题。这是我的代码:
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-f", dest="file", default="", help="file to be processed")
options = parser.parse_args()
main(options)
def main(options):
detail = options.file.decode(sys.stderr.encoding)
print os.path.exists(detail)
print detail
现在,当我通过 Windows 命令行运行脚本时:
sample.py -f "c:\temp\2-¡¢£¤¥¦§¨©ª«¬®¯°±²³´μ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ"
结果如下:
c:\temp\2-íóúñѪº¿⌐¬½¼?«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■
False
如您所见,解码后的文件名不同,导致文件存在检查为“False”。
有解决这个问题的想法吗?提前致谢!
【问题讨论】:
那是 Python 2 还是 3? @AaronDigulla Python 2(print
语句)
你可以使用 repr 函数来打印字符串。
这不是argparse
问题。这是系统和解释器在sys.argv
中为您提供什么的问题。
【参考方案1】:
了解发生了什么:Python 2 有几个与命令行、编码和 Windows 相关的错误(例如:subprocess.call fails with unicode strings in command line)
此类问题的黄金法则:使用 repr()
并将所有内容打印为 ASCII 字符串(使用 Unicode 转义)。否则,打印时数据可能会被破坏,从而增加混乱。
我建议从一个更简单的文件名 (C:\temp\ä.txt
) 开始,它应该是 C:\\temp\\\u00e4.txt
。
所以第一步是找出输入是什么:
print type(options.file)
如果这不是 Unicode,那么您永远不会得到正确编码的文件名。要解决此问题,您需要使用 Windows 用来向您传递文件名的编码。试试sys.stdin.encoding
和'mbcs'
(= Windows 文件系统编码)。
使用repr()
打印字符串,直到它看起来正确为止。
PEP 277 解释了 Python 如何在 Windows 上处理 Unicode 文件名。
简而言之,您必须确保将 Unicode 字符串 (type() == unicode
) 而不是字节字符串 (type() == str
) 传递给 open()
。
相关:
Python, Unicode, and the Windows console【讨论】:
对于简单文件名 (C:\temp\ä.exe),它可以工作。但是,对于我给出的示例文件名,它不是 除非你告诉我为什么它不起作用(错误信息,你尝试过的命令),否则我帮不了你。 没有错误信息。我刚刚将 os.path.exists(options.file) 用于 "C:\temp\ä.exe" 和 "c:\temp\2-¡¢£¤¥¦§¨©ª«¬®¯° ±²³´μ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáââãäåæçèééêêëììíîïðñòóôõö÷øùúûüýþÿ .exe”。前者结果为“真”,而后者结果为“假”,即使文件存在 你是如何创建这个文件的?在 Windows 上,可以创建名称后无法打开的文件。例如,虚线 (¦) 在文件名中可能是非法的。 使用控制面板 > 区域和语言,您可以添加语言和键盘支持。完成后,任务栏中将显示一个语言栏,您可以使用这些字符修改文件名【参考方案2】:开始更新
考虑到下面hpaul的反馈,以及他指出的bug链接, 我能够通过使用此函数将 sys.argv[1:] 参数转换为 unicode 来解决问题:
def win32_unicode_argv():
from ctypes import POINTER, byref, cdll, c_int, windll
from ctypes.wintypes import LPCWSTR, LPWSTR
GetCommandLineW = cdll.kernel32.GetCommandLineW
GetCommandLineW.argtypes = []
GetCommandLineW.restype = LPCWSTR
CommandLineToArgvW = windll.shell32.CommandLineToArgvW
CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
CommandLineToArgvW.restype = POINTER(LPWSTR)
cmd = GetCommandLineW()
argc = c_int(0)
argv = CommandLineToArgvW(cmd, byref(argc))
if argc.value > 0:
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
xrange(start, argc.value)]
if __name__ == "__main__":
sys.argv = win32_unicode_argv()
显然,这仅适用于 Windows,但我认为这对于在 Linux 下运行的脚本来说不是必需的。
更新结束
按照 Aaron 的建议,我尝试确保将其编码为 unicode,因此我这样做了:
parser.add_argument("-f", dest="file", type=lambda s : unicode(s, sys.getfilesystemencoding()), default="", help="file to be processed")
当我打印类型时,它会显示 unicode:
print type(options.file)
<type 'unicode'>
但是,当我再次进行存在性检查时,结果仍然是 False。我尝试了以下方法:
print os.path.exists(repr(options.file))
结果为假
print os.path.exists(repr(options.file.decode("utf8")))
结果:
UnicodeEncodeError: 'ascii' codec can't encode characters in position 25-36: ordinal not in range(128)
【讨论】:
正如我所说,您必须使用os.path.exists(options.file)
- 所有文件操作都需要unicode
类型的路径,否则它们会失败。此外,使用unicode.decode()
没有意义 - 数据已经是 Unicode。使用 unicode.encode('utf-8')
将 Unicode 转换为字节。
我已经将 os.path.exists(options.file) 用于 "C:\temp\ä.exe" 和 "c:\temp\2-¡¢£¤¥¦§¨ ©ª«¬®¯°±²³´μ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÒÛÜÝÞßàáââãäåæçèéêêëììíïïðñòóôõö÷øùúûüÿÿÿÿÿÿÿÿÿÊËÌÍÎÎÏÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáââãäåæçèéêêëììíïïðñòóôõö÷øùúûüþÿÿÿÿ.exe”。前者结果为“真”,而后者结果为“假”,即使文件存在
非常感谢@hpaulj 的反馈以上是关于Python argparse unicode 参数问题的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 argparse python 为两个类别设置不同的强制参数?