os.path.expanduser("~")的替代方案是什么?

4
在Python 2.7.x中,os.path.expanduser(“〜”)对于Unicode存在问题。
这意味着,如果“〜”的展开中含有非ASCII字符,将会引发异常。

http://bugs.python.org/issue13207

我该如何以其他方式实现相同的功能?
也就是说,我如何获取用户“主目录”的路径,在Win7上通常为C:\Users\usern-name

1
os.path.expanduser("~") 在我使用的 Windows 7 64 位操作系统和 Python 2.7 64 位版本中可以正常工作。 - Saullo G. P. Castro
1
@SaulloCastro:你的路径中是否包含超出系统代码页的代码点? - Martijn Pieters
1
@SaulloCastro:如果您的调用起作用,那么它就不会 - Martijn Pieters
1
把你的名字改成“Mark Ørnebjerg” ;) - GreenAsJade
1
@GreenAsJade:如果你有正确的代码页,那就可以工作.. :-P - Martijn Pieters
显示剩余9条评论
2个回答

2
您提供的链接中的错误报告包含一个解决脚本,该脚本直接从Win32 API检索相关的主目录信息。请注意保留HTML标记。
import ctypes
from ctypes import windll, wintypes

class GUID(ctypes.Structure):
    _fields_ = [
         ('Data1', wintypes.DWORD),
         ('Data2', wintypes.WORD),
         ('Data3', wintypes.WORD),
         ('Data4', wintypes.BYTE * 8)
    ]

    def __init__(self, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8):
        """Create a new GUID."""
        self.Data1 = l
        self.Data2 = w1
        self.Data3 = w2
        self.Data4[:] = (b1, b2, b3, b4, b5, b6, b7, b8)

    def __repr__(self):
        b1, b2, b3, b4, b5, b6, b7, b8 = self.Data4
        return 'GUID(%x-%x-%x-%x%x%x%x%x%x%x%x)' % (
                   self.Data1, self.Data2, self.Data3, b1, b2, b3, b4, b5, b6, b7, b8)

# constants to be used according to the version on shell32
CSIDL_PROFILE = 40
FOLDERID_Profile = GUID(0x5E6C858F, 0x0E22, 0x4760, 0x9A, 0xFE, 0xEA, 0x33, 0x17, 0xB6, 0x71, 0x73)

def expand_user():
    # get the function that we can find from Vista up, not the one in XP
    get_folder_path = getattr(windll.shell32, 'SHGetKnownFolderPath', None)
    if get_folder_path is not None:
        # ok, we can use the new function which is recomended by the msdn
        ptr = ctypes.c_wchar_p()
        get_folder_path(ctypes.byref(FOLDERID_Profile), 0, 0, ctypes.byref(ptr))
        return ptr.value
    else:
        # use the deprecated one found in XP and on for compatibility reasons
       get_folder_path = getattr(windll.shell32, 'SHGetSpecialFolderPathW', None)
       buf = ctypes.create_unicode_buffer(300)
       get_folder_path(None, buf, CSIDL_PROFILE, False)
       return buf.value

这个expand_user()函数仅返回当前用户的主目录。

谢谢 - 我会在测试后接受答案(它在本地工作,但我没有一个麻烦的用户名!)。你甚至编辑掉了pdb!! :) - GreenAsJade
好的 - 它起作用了 :) 我有点担心的是 - 在缺陷报告中的进一步评论提到“使用ctypes是有风险的”,或类似的,而且该问题已被标记为“无需修复”。为什么他们没有应用我正在使用的补丁,就像你上面引用的那样? - GreenAsJade
错误报告大多数情况下指出,他们不会修复它,因为这需要太多的工作量。ctypes 要求在运行时可用 DLL,这在某些情况下无法保证;Python 开发人员更喜欢依赖于可以在编译(安装)时进行测试的功能。 - Martijn Pieters

1
正如评论中所指出的那样,您实际上需要使用WinAPI调用来获取USERPROFILE环境变量的值:
import ctypes

buf = ctypes.create_unicode_buffer(1024)
ctypes.windll.kernel32.GetEnvironmentVariableW(u"USERPROFILE", buf, 1024)
home_dir = buf.value

或者,如果你更喜欢专用的 shell 函数:

CSIDL_PROFILE = 40
buf = ctypes.create_unicode_buffer(1024)
ctypes.windll.shell32.SHGetFolderPathW(None, CSIDL_PROFILE, None, 0, buf)
print buf.value

请注意,两个代码片段都返回配置文件路径,这不一定与主目录路径相同。

这就是问题所在,os.environ 字典包含使用系统代码页编码的字节串,如果代码点无法在该代码页中编码,则 Windows 会返回问号。这意味着那些环境变量对于配置的 Windows 代码页之外的任何内容都包含 /????? 字符,即使文件系统使用 UTF-16 来编码此信息。 - Martijn Pieters
实际上,我甚至不会得到问号,而是会出现异常:文件“ntpath.py”,第301行,在expanduser中出现Unicode解码错误... - GreenAsJade
我想知道这是否意味着如果我将b"~"传递给expanduser(),它会“只是工作”,还是说所有的变化只是我会得到???而不是异常,这同样糟糕! - GreenAsJade
@GreenAsJade:抱歉,我看的是ntpath.py的当前版本;你有一个旧版本,我的分析是不正确的。你确切使用的是哪个Python 2.7版本? - Martijn Pieters
2.7.6,尽管错误信息来自于2.7.2(在我收到该错误报告后升级到了2.7.6,但升级并没有解决问题) - GreenAsJade
@MartijnPieters:感谢您指出这一点,我添加了一个WinAPI解决方法。 - gog

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接