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 的建议,我尝试确保将其编码为 un​​icode,因此我这样做了:

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 为两个类别设置不同的强制参数?

python Argparse的例子。 #python #argparse #command-line

python中的argparse

python argparse模块

python argparse模块

python argparse模块