python中的shutil.copyfileobj方法也在合并文件时复制BOM字符

时间:2014-01-16 12:40:22

标签: python file-io utf-8 utf-16 byte-order-mark

我有以utf-16 LE编码创建的临时文件列表。我需要合并这些临时文件,结果文件应该是utf-16。

我做了什么

for fd in source_fds_list:
   with open(destination_url, 'ab') as destn_fd:
       shutil.copyfileobj(fd, destn_fd)
   fd.close()

它会导致目标文件中附加多个BOM。

我对上述情况的解决方案很少。

  • 需要检查并删除/跳过每个临时文件中的BOM字符
  • 以utf-8格式创建没有BOM字符的临时文件,然后使用copyfileobj合并它们

如果临时文件是用不同的编码风格编写的,该怎么办?

除了使用文件读取手动检查BOM之外,是否存在更好的解决方案?

1 个答案:

答案 0 :(得分:3)

shutil.copyfileobj()复制所有数据,无论如何。 BOM只是文件中的数据,shutil不是,也不会知道这些特定于文件格式的详细信息。

您可以自行轻松跳过BOM,但仍将大部分复制保留到shutil.copyfileobj()

import codecs

for fd in source_fds_list:
   with open(destination_url, 'ab') as destn_fd:
       with fd:
           start = fd.read(2)
           if start != codecs.BOM_UTF16_LE:
               destn_fd.write(start)
           shutil.copyfileobj(fd, destn_fd)

首先从源文件中读取前2个字节,shutil.copyfileobj()将继续读取文件中的所有其他内容,跳过BOM。无论如何,所有shutil.copyfileobj()都会调用data = source.read(buffer)destination.write(data)

如果您不知道用于输入文件的编解码器,则会遇到启发式问题。你可以测试各种codecs BOM constants但是出现了误报的可能性;用UTF- *以外的编解码器编码的文件和初始字节看起来像的BOM:

for fd in source_fds_list:
   with open(destination_url, 'ab') as destn_fd:
       with fd:
           start = fd.read(4)

           if start not in (codecs.BOM_UTF32_LE, codecs.BOM_UTF32_BE):
               if start[:3] != BOM_UTF8:
                   if start[:2] in (codecs.BOM_UTF16_LE, codecs.BOM_UTF16_BE):
                       # UTF-16 BOM, skip 2 bytes
                       start = start[2:]
               else:
                   # UTF-8 BOM, skip 3 bytes
                   start = start[-1]
               # Not a UTF-32 BOM, write read bytes (minus skipped bytes)
               destn_fd.write(start)

           shutil.copyfileobj(fd, destn_fd)