2012-01-11 10 views
15

Tenemos una persona que ingresa datos codificados en UTF-16 en Windows y quisieran tener utf-8 y eliminar la lista de materiales. La conversión de utf-8 funciona pero la BOM todavía está allí. ¿Cómo eliminaría esto? Esto es lo que tengo actualmente:convirtiendo utf-16 -> utf-8 Y eliminando BOM

batch_3={'src':'/Users/jt/src','dest':'/Users/jt/dest/'} 
batches=[batch_3] 

for b in batches: 
    s_files=os.listdir(b['src']) 
    for file_name in s_files: 
    ff_name = os.path.join(b['src'], file_name) 
    if (os.path.isfile(ff_name) and ff_name.endswith('.json')): 
     print ff_name 
     target_file_name=os.path.join(b['dest'], file_name) 
     BLOCKSIZE = 1048576 
     with codecs.open(ff_name, "r", "utf-16-le") as source_file: 
     with codecs.open(target_file_name, "w+", "utf-8") as target_file: 
      while True: 
      contents = source_file.read(BLOCKSIZE) 
      if not contents: 
       break 
      target_file.write(contents) 

Si Hexdump -C veo:

Wed Jan 11$ hexdump -C svy-m-317.json 
00000000 ef bb bf 7b 0d 0a 20 20 20 20 22 6e 61 6d 65 22 |...{.. "name"| 
00000010 3a 22 53 61 76 6f 72 79 20 4d 61 6c 69 62 75 2d |:"Savory Malibu-| 

en el archivo resultante. ¿Cómo elimino la lista de materiales?

THX

Respuesta

19

Sólo tiene que utilizar str.decode y str.encode:

with open(ff_name, 'rb') as source_file: 
    with open(target_file_name, 'w+b') as dest_file: 
    contents = source_file.read() 
    dest_file.write(contents.decode('utf-16').encode('utf-8')) 

str.decode se librará de la lista de materiales para usted (y deducir el orden de bits).

+0

fresco - funciona bien, ¿sabe cómo añadir un CRLF -> conversión de LF facilidad en la lectura? thx si puede ayudar – timpone

+0

Ese enfoque (almacenar todo el archivo en la memoria dos veces) no es muy eficiente, si está trabajando con archivos de gran tamaño. –

28

Esta es la diferencia entre UTF-16LE y UTF-16

  • UTF-16LE es poco endian sin una lista de materiales
  • UTF-16 se endian grande o pequeña con una lista de materiales

Así que cuando se use UTF-16LE, la lista de materiales es solo una parte del texto. Use UTF-16 en su lugar, por lo que la lista de materiales se elimina automáticamente. La razón por la cual existen UTF-16LE y UTF-16BE es para que la gente pueda llevar el texto "codificado correctamente" sin listas de materiales, que no se aplica a usted.

Nota lo que sucede cuando se codifican utilizando una codificación y decodificación usando la otra. (UTF-16 detecta automáticamente UTF-16LE veces, no siempre.)

>>> u'Hello, world'.encode('UTF-16LE') 
'H\x00e\x00l\x00l\x00o\x00,\x00 \x00w\x00o\x00r\x00l\x00d\x00' 
>>> u'Hello, world'.encode('UTF-16') 
'\xff\xfeH\x00e\x00l\x00l\x00o\x00,\x00 \x00w\x00o\x00r\x00l\x00d\x00' 
^^^^^^^^ (BOM) 

>>> u'Hello, world'.encode('UTF-16LE').decode('UTF-16') 
u'Hello, world' 
>>> u'Hello, world'.encode('UTF-16').decode('UTF-16LE') 
u'\ufeffHello, world' 
    ^^^^ (BOM) 

o se puede hacer esto en la cáscara:

for x in * ; do iconv -f UTF-16 -t UTF-8 <"$x" | dos2unix >"$x.tmp" && mv "$x.tmp" "$x"; done 
Cuestiones relacionadas