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

时间:2012-01-22 08:44:30

标签: python google-app-engine file-upload character-encoding blobstore

我有一个基于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()

这适用于使用Code Page 1252编码的文本文件,这是使用Windows记事本并使用所谓的“ANSI”编码保存时获得的。但是如果你将这个处理程序与一个用记事本的UTF-8编码保存的文件一起使用,并且包含一些西里尔字符或一个u-umlaut,你最终会得到胡言乱语。对于这样的文件,将decode('cp1252')改为decode('utf_8')就可以了。 (嗯,开头也有可能出现字节顺序标记(BOM),但这很容易被剥夺。)

但是你怎么知道使用哪种解码? BOM不保证在那里,我没有看到任何其他方式知道,除了询问用户 - 谁可能也不知道。有没有可靠的方法来确定编码?如果其他方法解决了这个问题,我不一定要使用blobstore。

然后是Windows Notepad称为“Unicode”的编码,这是一种UTF-16小端编码。我找不到正确解码用这种编码保存的文件的解码(包括“utf_16_le”)。可以读取其中一个文件吗?

2 个答案:

答案 0 :(得分:3)

答案 1 :(得分: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)

如果您想从上传的文件中逐步阅读,那么您将陷入痛苦的世界。问题是“对于blob_reader中的行”显然会读到找到换行符(\ x0a)的字节,这在读取utf_16_le编码文件时是灾难性的,因为它会将\ x0a \ x00序列切成两半!

我不推荐它,但是这里是一个上传处理程序,它将成功地一次一行地处理由Windows 7 Notepad中的所有编码(即ANSI,UTF-8,Unicode和Unicode big endian)存储的文件。如您所见,剥离线路终端序列非常麻烦。

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 Notepad如何创建文件。请注意,在一次读取一行之前,我最多可以抓取10000个字符以供chardet分析。这只是猜测它可能需要多少字节。这种笨拙的双读是避免这种解决方案的另一个原因。