Python魔法无法识别Unicode文件名

5
在我的小项目中,我需要识别目录中的文件类型。所以我使用了 python-magic 模块,并做了以下操作:
from Tkinter import Tk
from tkFileDialog import askdirectory

def getDirInput():
    root = Tk()
    root.withdraw()
    return askdirectory()
di = getDirInput()
print('Selected Directory: ' + di)
for f in os.listdir(di):
    m = magic.Magic(magic_file='magic')
    print 'Type of ' + f + '  -->  ' + m.from_file(f)

但是似乎 python-magic 在我将Unicode文件名传递给 from_file() 函数时无法处理它。以下是一个示例输出:

Selected Directory: C:/Users/pruthvi/Desktop/vidrec/temp
Type of log.txt  -->  ASCII English text, with very long lines, with CRLF, CR line terminators
Type of TAEYEON 태연_ I (feat. Verbal Jint)_Music Video.mp4  -->  cannot open `TAEYEON \355\234\227_ I (feat. Verbal Jint)_Music Video.mp4' (No such file or directory)
Type of test.py  -->  a python script text executable

你可以观察到,python-magic 无法识别第二个文件 TAEYEON... 的类型,因为其中包含 Unicode 字符。它将 태연 字符显示为 \355\234\227,而我在两种情况下都传递了相同的字符。我该如何解决这个问题并找到具有 Unicode 字符的文件类型?谢谢。

@AlastairMcCormack,Python 2.x - Pruthvi Raj
@falsetru,IO错误指出文件不存在,因为Unicode字符被错误地替换为“??”。 - Pruthvi Raj
这个怎么样?m.from_file(os.path.join(di, f)) - falsetru
@falsetru,我之前也尝试过,但没有成功。它显示的输出与我在问题中发布的相同。 - Pruthvi Raj
@RolfofSaxony,我以为这是后台正在发生的事情,但现在我如何识别具有Unicode文件名的文件类型? - Pruthvi Raj
显示剩余4条评论
2个回答

7
但是似乎python-magic无法处理Unicode文件名。
正确。实际上,在Windows上,大多数跨平台软件无法处理文件名中的非ASCII字符。
这是因为C标准库对所有文件名使用字节字符串,但Windows使用Unicode字符串(技术上是UTF-16代码单元字符串,但在这里不重要)。当使用C标准库的软件通过基于字节的字符串打开文件时,MS C运行时会自动将其转换为Unicode字符串,使用一个编码(令人困惑的“ANSI”代码页),该编码取决于Windows安装的语言环境。您的ANSI代码页可能是1252,无法编码韩文字符,因此无法使用该文件名。遗憾的是,ANSI代码页从未像UTF-8一样合理,因此它永远无法包含所有可能的Unicode字符。
Python很特殊,因为它具有额外的支持Windows Unicode文件名的功能,可以绕过C标准库并直接调用底层的Win32 API处理Unicode文件名。因此,您可以使用open()传递一个Unicode字符串,并且它将适用于所有文件名。
然而,python-magicfrom_file调用不是从Python打开文件。相反,它将文件名传递给纯C编写的libmagic库。由于libmagic没有Unicode的特殊Windows文件名代码路径,因此它失败了。
建议您在Python中自己打开文件,并改用magic.from_buffer

谢谢,我有没有办法克服这个问题并找到文件名中带Unicode字符的文件类型? - Pruthvi Raj
使用 m.from_buffer() 函数,将文件开头的一些数据(例如1024字节)作为参数传入即可。 - bobince
哇,谢谢你解决了所有的问题!我太蠢了,不知道 from_buffer() :3 - Pruthvi Raj

2

从魔术模块的响应来看,您的字符在某处被错误地翻译了——只显示了一半的字符串,并且 的字节顺序是错误的——至少应该是 \355\227\234

由于这是在 Windows 上,这引起了 UTF-16 字节顺序的警报。

可能可以通过编码为 UTF-16 来解决此问题。正如其他评论者建议的那样,您需要在目录前加上前缀。

input_encoding = locale.getpreferredencoding()
u_di = di.decode(input_encoding)
m = magic.Magic(magic_file='magic') # only needs to be initialised once

for f in os.listdir(u_di):
    fq_f = os.path.join(u_di, f)
    utf16_fq_f = fq_f.encode("UTF-16LE")
    print m.from_file(utf16_fq_f)

谢谢,我知道在Python 2中并不是所有的字符串都是Unicode,并且您需要传递一个Unicode目录路径,但这也不能解决问题,它会创建一个“IO错误”,指出文件不存在,就像我之前对falsetru所评论的那样。 - Pruthvi Raj
目录中是否包含非ASCII字符? - Alastair McCormack
不,它没有,所选目录在问题的输出中打印出来了 :) - Pruthvi Raj
你能提供完整的“IO错误”异常吗? - Alastair McCormack
抱歉,我的错。实际上它并没有生成“IO错误”,但输出与上面的问题中显示的相同。 - Pruthvi Raj
显示剩余10条评论

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