我有一个基于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”)。可以读取其中一个文件吗?
答案 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分析。这只是猜测它可能需要多少字节。这种笨拙的双读是避免这种解决方案的另一个原因。