确定上传到Google App Engine文件的编码

1 投票
2 回答
1121 浏览
提问于 2025-04-17 10:49

我有一个基于Google App Engine(GAE)和Python的网站,我希望用户能够上传一个文本文件进行处理。我的实现是根据官方文档中的标准代码(可以查看 http://code.google.com/appengine/docs/python/blobstore/overview.html)来做的,我的文本文件上传处理程序大致是这样的:

class Uploader(blobstore_handlers.BlobstoreUploadHandler):
    def post(self):
        upload_files = self.get_uploads('file')
        blob_info = upload_files[0]
        blob_reader = blobstore.BlobReader(blob_info.key())
        for line in blob_reader:
            line = line.rstrip().decode('cp1252')
            do_something(line)
        blob_reader.close()

这个处理程序对于用代码页1252编码的文本文件工作得很好,这种编码是在使用Windows记事本并选择“ANSI”编码保存时得到的。但是,如果你用这个处理程序处理一个用记事本的UTF-8编码保存的文件,比如说里面有一些西里尔字母或带有变音符号的字母,你就会得到一堆乱码。对于这样的文件,把decode('cp1252')改成decode('utf_8')就能解决问题。(当然,文件开头可能还有一个字节顺序标记(BOM),但这个可以很容易去掉。)

那么,怎么知道该用哪种解码呢?BOM并不一定存在,我也想不出其他方法来判断,除了问用户——而用户可能也不知道。有没有什么可靠的方法来确定编码呢?如果有其他方法能解决这个问题,我不一定非要使用blobstore。

还有Windows记事本称之为“Unicode”的编码,这是一种UTF-16小端编码。我找不到任何解码方式(包括“utf_16_le”)能够正确解码用这种编码保存的文件。这种文件能被读取吗?

2 个回答

1

根据demalexx的建议,我的上传处理程序现在使用chardet来判断文件的编码(可以在http://pypi.python.org/pypi/chardet找到)。从我目前的观察来看,这个方法效果非常好。

在这个过程中,我发现用“for line in blob_reader”来读取上传的文本文件非常麻烦。如果你不介意一次性读取整个文件,那么解决方案就简单多了。(注意要去掉一个BOM序列,并且要处理换行符CR/LF的问题。)

class Uploader(blobstore_handlers.BlobstoreUploadHandler):
    def post(self):
        upload_files = self.get_uploads('file')
        blob_info = upload_files[0]
        text = blobstore.BlobReader(blob_info.key()).read()
        encoding = chardet.detect(text)['encoding']
        if encoding is not None:
            for line in text.decode(encoding).lstrip(u'\ufeff').split(u'\x0d\x0a'):
                do_something(line)

如果你想逐行读取上传的文件,那就会遇到很多麻烦。问题在于“for line in blob_reader”似乎是读取到换行符(\x0a)为止,这在读取utf_16_le编码的文件时会出问题,因为它会把一个\x0a\x00的序列切成两半!

我不推荐这种方法,但这里有一个上传处理程序,可以逐行处理Windows 7记事本保存的所有编码格式的文件(也就是ANSI、UTF-8、Unicode和Unicode大端)。如你所见,去掉行结束符的过程非常繁琐。

class Uploader(blobstore_handlers.BlobstoreUploadHandler):
    def post(self):
        upload_files = self.get_uploads('file')
        blob_info = upload_files[0]
        blob_reader = blobstore.BlobReader(blob_info.key())
        encoding = chardet.detect(blob_reader.read(10000))['encoding']
        if encoding is not None:
            blob_reader.seek(0)
            for line in blob_reader:
                if line[:2] in ['\xff\xfe','\xfe\xff']:
                    start = 2
                elif line[:3] == '\xef\xbb\xbf':
                    start = 3
                else:
                    start = 0
                if encoding == 'UTF-16BE':
                    if line[-4:] == '\x00\x0d\x00\x0a':
                        line = line[start:-4]
                    elif start > 0:
                        line = line[start:]
                elif encoding == 'UTF-16LE':
                    if line[start] == '\x00':
                        start += 1
                    if line[-3:] == '\x0d\x00\x0a':
                        line = line[start:-3]
                    elif start > 0:
                        line = line[start:]
                elif line[-2:] == '\x0d\x0a':
                    line = line[start:-2]
                elif start > 0:
                    line = line[start:]
                do_something(line.decode(encoding))

这无疑是个脆弱的解决方案,我的测试仅限于这四种编码,并且只针对Windows 7记事本创建的文件。请注意,在逐行读取之前,我会先抓取最多10000个字符供chardet分析。这只是我对它可能需要多少字节的一个猜测。这种笨拙的双重读取也是另一个不推荐使用这个解决方案的原因。

3

也许这个链接对你有帮助:Python:有没有办法确定文本文件的编码方式?

撰写回答