Microsoft Excel destruye diacríticos en archivos .csv?

190

Estoy exportando datos mediante programación (usando PHP 5.2) a un archivo de prueba .csv.
Datos de ejemplo: Numéro 1(tenga en cuenta la e acentuada). Los datos son utf-8(sin lista de materiales antepuesta).

Cuando abro este archivo en MS Excel se muestra como Numéro 1.

Puedo abrir esto en un editor de texto (UltraEdit) que lo muestra correctamente. UE informa que el personaje es decimal 233.

¿Cómo puedo exportar datos de texto en un archivo .csv para que MS Excel los represente correctamente , preferiblemente sin forzar el uso del asistente de importación o la configuración de asistente no predeterminada?

Freddo411
fuente
Me interesaría saber más acerca de su solución BOM, ya que creo que probé "EF BB BF" que no funcionó para mí.
James Baker,
3
La solución de trabajo elegida fue: * incluir una lista de materiales; utf-8 * usa este encabezado: 'Content-type: text / plain; charset = utf-8 'Esto "funcionó" en excel 2003 y excel 2007 - donde trabajado = abierto sin un asistente de importación y renderizado diacríticos correctamente. No verifiqué que se requería la lista de materiales.
Freddo411
2
Se requiere la lista de materiales, acabo de probar esto ahora. Sin ella, los caracteres especiales no se muestran bien.
Alex Ciminian
2
Me encantaría que alguien pudiera decir más sobre cómo agregar una lista de materiales (marcador de orden de bytes). Si solo hago algo como Response.Write (EF BB BF "), esos caracteres solo aparecen al comienzo del archivo.
sydneyos
sydneyos: como dice Fergal a continuación; Anteponga \ uFEFF a su cadena.
noocito

Respuestas:

243

Un archivo UTF8 formateado correctamente puede tener una marca de orden de bytes como sus primeros tres octetos. Estos son los valores hexadecimales 0xEF, 0xBB, 0xBF. Estos octetos sirven para marcar el archivo como UTF8 (ya que no son relevantes como información de "orden de bytes"). 1 Si esta lista de materiales no existe, el consumidor / lector debe deducir el tipo de codificación del texto. Los lectores que no sean compatibles con UTF8 leerán los bytes como alguna otra codificación, como Windows-1252, y mostrarán los caracteres al comienzo del archivo.

Existe un error conocido en el que Excel, al abrir archivos CSV UTF8 a través de la asociación de archivos, supone que están en una codificación de un solo byte, sin tener en cuenta la presencia de la lista de materiales UTF8. Esto no se puede solucionar mediante ninguna página de códigos predeterminada del sistema o configuración de idioma. La lista de materiales no tendrá ni idea en Excel, simplemente no funcionará. (Un informe minoritario afirma que la lista de materiales a veces activa el asistente "Importar texto"). Este error parece existir en Excel 2003 y versiones anteriores. La mayoría de los informes (en medio de las respuestas aquí) dicen que esto se solucionó en Excel 2007 y versiones posteriores.

Tenga en cuenta que siempre * puede abrir correctamente archivos CSV UTF8 en Excel utilizando el asistente "Importar texto", que le permite especificar la codificación del archivo que está abriendo. Por supuesto, esto es mucho menos conveniente.

Es muy probable que los lectores de esta respuesta se encuentren en una situación en la que no son particularmente compatibles con Excel <2007, pero envían texto UTF8 sin formato a Excel, que lo malinterpreta y rocía su texto con Ãotros caracteres similares de Windows-1252. Agregar la UTF8 BOM es probablemente su mejor y más rápida solución.

Si está atrapado con usuarios en Excels anteriores y Excel es el único consumidor de sus CSV, puede solucionar este problema exportando UTF16 en lugar de UTF8. Excel 2000 y 2003 harán doble clic para abrirlos correctamente. (Algunos otros editores de texto pueden tener problemas con UTF16, por lo que es posible que tenga que sopesar sus opciones con cuidado).


* Excepto cuando no puede, (al menos) el Asistente de importación de Excel 2011 para Mac no siempre funciona con todas las codificaciones, independientemente de lo que le diga. </anecdotal-evidence> :)

James Baker
fuente
14
Me llevó una eternidad encontrar dónde especificar la codificación. Cuadro de diálogo Guardar> Botón Herramientas> Opciones web> Pestaña Codificación. Seguro que son buenos para ocultar cosas tan importantes.
Triynko
66
Incorrecto: agregar una lista de materiales a un archivo UTF-8 carga ese archivo correctamente sin requerir el asistente de importación en Excel 2007.
Victor Nicollet
3
Encontramos lo mismo que Victor dice hoy (usando Excel 2010, es todo lo que teníamos disponible). Agregar una UTF-8 BOM / Signature (EF BB BF) pareció corregir el doble clic usando la codificación predeterminada del sistema, y ​​usa correctamente UTF8 :)
Danny Tuppeny
20
En general , un archivo codificado en UTF-8 no debe tener una marca de orden de bytes antepuesta. UTF-8 no tiene un orden de bytes variable, y ponerlo allí sabotea la compatibilidad ASCII de UTF-8. Hay algunos formatos de archivo específicos que permiten o fomentan una UTF-8 faux-BOM, pero de lo contrario se debe evitar. CSV ignora por completo la codificación, por lo que nadie sabe si una herramienta determinada interpretará la secuencia de bytes 0xEF 0xBB 0xBF como un indicador de UTF-8; un personaje de control invisible en la primera celda; los personajes en la primera celda; o algo completamente diferente.
bobince
3
@Ian: Nadie sabe con certeza si es UTF-8 con una lista de materiales: 0xEF 0xBB 0xBF es una secuencia válida en la mayoría de las codificaciones heredadas (por lo tanto, a menudo se malinterpreta como ISO-8859-1 o cp1252 y se muestra como ). Solo ayuda a adivinar algoritmos y para formatos de archivo que específicamente le permiten (p. Ej. XML). La desventaja de incluir una lista de materiales falsa en los archivos UTF-8 es que rompes su compatibilidad ASCII (un punto de venta importante para UTF-8) Muchas herramientas de texto ignorantes de codificación se romperán frente a una lista de materiales falsa líder inesperada.
bobince
39

Antes de una BOM (\ uFEFF) funcionó para mí (Excel 2007), en ese Excel reconoció el archivo como UTF-8. De lo contrario, guardarlo y usar el asistente de importación funciona, pero es menos ideal.


fuente
1
Todavía abre el asistente de importación de texto, por lo que la diferencia es que simplemente puede hacer doble clic, por lo que aún no es ideal, pero la única solución conocida de todos modos.
haridsv
Para mí, no aparece ningún asistente de importación con Excel 2007.
Victor Nicollet
Tampoco tengo un asistente de importación: funciona como se esperaba si hay una BOM / Firma UTF8 (EF BB BF).
Danny Tuppeny
Además, \ufeffes una lista de materiales UTF-16 (BE) no una lista de materiales UTF-8
Alastair McCormack
2
No, @AlastairMcCormack, tampoco, dependiendo de cómo esté codificado. "\ ufeff" codificado como UTF-8 es exactamente EF BB BF. (Codificado como UTF-16 será de solo dos bytes.)
Dave Burt el
30

A continuación se muestra el código PHP que uso en mi proyecto cuando envío Microsoft Excel al usuario:

  /**
   * Export an array as downladable Excel CSV
   * @param array   $header
   * @param array   $data
   * @param string  $filename
   */
  function toCSV($header, $data, $filename) {
    $sep  = "\t";
    $eol  = "\n";
    $csv  =  count($header) ? '"'. implode('"'.$sep.'"', $header).'"'.$eol : '';
    foreach($data as $line) {
      $csv .= '"'. implode('"'.$sep.'"', $line).'"'.$eol;
    }
    $encoded_csv = mb_convert_encoding($csv, 'UTF-16LE', 'UTF-8');
    header('Content-Description: File Transfer');
    header('Content-Type: application/vnd.ms-excel');
    header('Content-Disposition: attachment; filename="'.$filename.'.csv"');
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header('Pragma: public');
    header('Content-Length: '. strlen($encoded_csv));
    echo chr(255) . chr(254) . $encoded_csv;
    exit;
  }

ACTUALIZADO: la mejora del nombre de archivo y el ERROR corrigen el cálculo correcto de la longitud Gracias a TRiG y @ ivanhoe011

Marc Carlucci
fuente
1
Intenté varias otras sugerencias en esta página, pero esto funcionó para mí en Excel 2007. Los cambios más importantes fueron usar pestañas en lugar de comas (a pesar de que es un archivo .csv) y la línea de arriba que hace eco de los dos caracteres seguidos por el llame a mb_convert_encoding (). También tuve que recompilar PHP con --enable-mbstring para obtener soporte para mb_convert_encoding (). ¡Gracias!
Russell G
1
Esto también funcionó bien para mí, gracias. Sin embargo, en Safari recibo un error en mi consola 'Recurso interpretado como documento pero transferido como ...' Supongo que es una peculiaridad de WebKit, juzgando stackoverflow.com/questions/3899426/… , pero tal vez no lo es y / o alguien tiene Encontré una solución. Además, en su ejemplo sugeriría un cambio: 'Content-Disposition: attachment; filename="'.$filename.'.csv"'porque Firefox quiere las comillas dobles, o de lo contrario cortará su nombre de archivo después de un espacio.
kasimir
¿Por qué estás generando CSV ( text/csv) pero llamándolo Excel ( application/vnd.ms-excel)?
TRiG
2
¡Esto funciona muy bien! Puedo confirmar que también funciona en Mac (en Office 2011).
Jonathan
¿No debería ser esto header('Content-Length: '. mb_strlen($encoded_csv, 'UTF-16LE'));?
Rich Bradshaw
13

La respuesta para todas las combinaciones de versiones de Excel (2003 + 2007) y tipos de archivo

La mayoría de las otras respuestas aquí se refieren solo a su versión de Excel y no necesariamente lo ayudarán, porque su respuesta podría no ser cierta para su versión de Excel.

Por ejemplo, agregar el carácter BOM presenta problemas con el reconocimiento automático del separador de columnas, pero no con todas las versiones de Excel.

Hay 3 variables que determinan si funciona en la mayoría de las versiones de Excel:

  • Codificación
  • Presencia de personaje BOM
  • Separador celular

Alguien estoico en SAP probó todas las combinaciones e informó el resultado. ¿Resultado final? Use UTF16le con BOM y caracteres de tabulación como separador para que funcione en la mayoría de las versiones de Excel.

¿No me crees? Yo tampoco, pero lea aquí y llore: http://wiki.sdn.sap.com/wiki/display/ABAP/CSV+tests+of+encoding+and+column+separator

Christiaan Westerbeek
fuente
¿Por qué no solo agregar sep=,o lo que quieras usar? Si ya está agregando la lista de materiales, supongo que no es reacio a agregar cosas al archivo.
Casey
Bueno, en realidad, para responder mi propia pregunta, no agregarías la declaración del separador de campo porque hace que este truco deje de funcionar. Básicamente, es una codificación confusa o su archivo no se interpreta correctamente como un CSV si sus usuarios tienen la configuración de región incorrecta.
Casey
1
utf-16le + BOM (0xFF 0xFE) + tab es lo mejor
zhaozhi
10

seleccione UTF-8 enconding al importar. si usa Office 2007, aquí es donde lo eligió: justo después de abrir el archivo.

daniels
fuente
1
Esto es útil. He modificado la pregunta para preguntar cómo hacer esto sin recurrir al asistente
Freddo411
9

Echo UTF-8 BOM antes de enviar datos CSV. Esto soluciona todos los problemas de caracteres en Windows, pero no funciona para Mac.

echo "\xEF\xBB\xBF";

Funciona para mí porque necesito generar un archivo que se usará solo en PC con Windows.

Johal
fuente
No es cierto para cada tipo de separador de columnas ni para todas las versiones de Excel. Lea mi respuesta a continuación (a continuación, por ahora).
Christiaan Westerbeek
7

UTF-8 no funciona para mí en Office 2007 sin ningún paquete de servicio, con o sin BOM (U + ffef o 0xEF, 0xBB, 0xBF, ni funciona) la instalación de sp3 hace que UTF-8 funcione cuando 0xEF, 0xBB, 0xBF BOM es antepuesto

UTF-16 funciona al codificar en Python usando "utf-16-le" con una lista de materiales 0xff 0xef BOM antepuesta, y usando tab como separador. Tuve que escribir manualmente la lista de materiales, y luego usar "utf-16-le" en lugar de "utf-16", de lo contrario cada codificación () antepuso la lista de materiales a cada fila escrita que aparecía como basura en la primera columna de la segunda linea y despues.

No puedo decir si UTF-16 funcionaría sin ningún SP instalado, ya que no puedo volver ahora. suspiro

Esto está en Windows, no sé sobre Office para MAC.

para ambos casos de trabajo, la importación funciona cuando se inicia una descarga directamente desde el navegador y el asistente de importación de texto no interviene, funciona como es de esperar.

Gerald Dol
fuente
Funciona en Excel 2011 para Mac también.
Adam
gracias por su publicación, usar utf-16le está bien incluso cuando no instaló Office 2007 sp3, pero la lista de materiales debe ser 0xFF 0xFE
zhaozhi
4

Como dijo Fregal, \ uFEFF es el camino a seguir.

<%@LANGUAGE="JAVASCRIPT" CODEPAGE="65001"%>
<%
Response.Clear();
Response.ContentType = "text/csv";
Response.Charset = "utf-8";
Response.AddHeader("Content-Disposition", "attachment; filename=excelTest.csv");
Response.Write("\uFEFF");
// csv text here
%>
Kristof Neirynck
fuente
1
Simplemente observe y vea cómo se ignora su separador de pestañas en Excel 2007 cuando usa BOM. Tienes que pensar en algo más.
Christiaan Westerbeek
3

También noté que la pregunta fue "respondida" hace algún tiempo, pero no entiendo las historias que dicen que no puede abrir un archivo csv codificado con utf8 con éxito en Excel sin usar el asistente de texto.

Mi experiencia reproducible: escriba Old MacDonald had a farm,ÈÌÉÍØen el Bloc de notas, presione Entrar y luego Guardar como (con la opción UTF-8).

Usando Python para mostrar lo que hay realmente allí:

>>> open('oldmac.csv', 'rb').read()
'\xef\xbb\xbfOld MacDonald had a farm,\xc3\x88\xc3\x8c\xc3\x89\xc3\x8d\xc3\x98\r\n'
>>> ^Z

Bueno. El bloc de notas ha puesto una lista de materiales en la parte delantera.

Ahora vaya al Explorador de Windows, haga doble clic en el nombre del archivo, o haga clic con el botón derecho y use "Abrir con ...", y emerge Excel (2003) con la visualización como se esperaba.

John Machin
fuente
@ Cocowalla: Bueno, acabo de intentar esto (nuevamente; lo probé antes de publicar) y funcionó con Excel 2007 (que es lo que estoy usando ahora). ¿Hiciste open('oldmac.csv', 'rb').read()para verificar tu entrada?
John Machin
No probé con Excel 2007 (sé que Excel 2007 lee archivos UTF-8 con una lista de materiales muy bien), probé con Excel 2003
Cocowalla
@ Cocowalla: Bueno, me funcionó con Excel 2003 cuando lo tuve. ¿Seguro que tiene el último service pack para Excel 2003? ¿Verificó su entrada como sugerí?
John Machin
Verifiqué que el bloc de notas había atascado una lista de materiales al comienzo del archivo, pero estoy en Excel 2003 SP2 (SP3 está disponible), así que supongo que esto solo funciona en SP3
Cocowalla
2

Puede guardar un archivo html con la extensión 'xls' y los acentos funcionarán (al menos desde 2007).

Ejemplo: guarde esto (usando Guardar como utf8 en el Bloc de notas) como test.xls:

<html>
<meta http-equiv="Content-Type" content="text/html" charset="utf-8" />
<table>
<tr>
  <th>id</th>
  <th>name</th>
</tr>
<tr>
 <td>4</td>
 <td>Hélène</td>
</tr>
</table>
</html>
Benjol
fuente
Opción interesante. Abre el texto a la derecha pero, por alguna razón, toda la página es completamente blanca. Sin las líneas clásicas de hojas de cálculo que delimitan filas y columnas (office para mac)
Sebastian Sastre
Sí, lo mismo en Office 2007 en Windows. Siempre me sorprende que haya funcionado, para ser honesto. (Tenga en cuenta, si se agrega border="1"a la tabla, que hace llegar las líneas, pero sólo alrededor de las 4 células :)
Benjol
1

Esto es solo una cuestión de codificaciones de caracteres. Parece que está exportando sus datos como UTF-8: é en UTF-8 es la secuencia de dos bytes 0xC3 0xA9, que cuando se interpreta en Windows-1252 es é. Cuando importe sus datos a Excel, asegúrese de decirle que la codificación de caracteres que está usando es UTF-8.

Adam Rosenfield
fuente
He confirmado que los datos son UTF-8. ¿Qué pongo en el archivo para que Excel sepa que mis datos son utf-8 (BOM?)
Freddo411
Creo que necesita cambiar la codificación del archivo, Excel utiliza la página de códigos predeterminada del sistema para manejar archivos csv
albertein
No estoy completamente seguro, ya que no tengo Excel instalado en la máquina que estoy usando actualmente, pero con OpenOffice, hay un cuadro desplegable para la codificación de caracteres cuando importa un archivo CSV. Desde allí, elija Unicode (UTF-8).
Adam Rosenfield
Excel no tiene el menú desplegable AFAIK
albertein
1

El formato CSV se implementa como ASCII, no unicode, en Excel, lo que destruye los signos diacríticos. Experimentamos el mismo problema que es cómo rastreé que el estándar CSV oficial se definiera como basado en ASCII en Excel.

Jeff Yates
fuente
En realidad, CSV no está vinculado a una codificación específica. Es Excel el que asume ASCII. en.wikipedia.org/wiki/Comma-separated_values
spoulson
Eso es lo que dije. "implementado como ASCII en Excel", "CSV definido como basado en ASCII en Excel". No estoy seguro de qué punto está haciendo, ya que parece estar de acuerdo conmigo.
Jeff Yates el
2
En realidad, usted dice "El formato CSV se implementa como ASCI", creo que de ahí proviene la confusión.
RichardOD
1

Excel 2007 lee correctamente UTF-8 con csv codificado BOM (EF BB BF).

Excel 2003 (y tal vez antes) lee UTF-16LE con BOM (FF FE), pero con TAB en lugar de comas o punto y coma.

usuario203319
fuente
1

Solo puedo hacer que CSV se analice correctamente en Excel 2007 como little-endian UTF-16 separado por tabulaciones comenzando con la marca de orden de bytes adecuada.

Manfred Stienstra
fuente
1

Escribir una lista de materiales en el archivo CSV de salida realmente funcionó para mí en Django:

def handlePersoonListExport(request):
    # Retrieve a query_set
    ...

    template = loader.get_template("export.csv")
    context = Context({
        'data': query_set,
    })

    response = HttpResponse()
    response['Content-Disposition'] = 'attachment; filename=export.csv'
    response['Content-Type'] = 'text/csv; charset=utf-8'
    response.write("\xEF\xBB\xBF")
    response.write(template.render(context))

    return response

Para obtener más información http://crashcoursing.blogspot.com/2011/05/exporting-csv-with-special-characters.html ¡ Gracias chicos!

Lukas Batteau
fuente
Sí, esto funcionó para mí con Excel 2010. En el uso de Java printWriter.print('\ufeff'), consulte también Cómo agregar una lista de materiales UTF-8 en Java .
tsauerwein
1

Otra solución que encontré fue simplemente codificar el resultado como Página de códigos de Windows 1252 (Windows-1252 o CP1252). Esto se haría, por ejemplo, configurando Content-Typeadecuadamente algo así text/csv; charset=Windows-1252y configurando la codificación de caracteres del flujo de respuesta de manera similar.

espeluznante
fuente
Gracias por este Funciona en excel windows y mac. Lo estoy usando.
Sebastian Sastre
Esto solo funcionaría si su rango de caracteres no ASCII cae completamente dentro de Windows-1252. Entonces, por ejemplo, no hay coreano / chino / japonés, no cirílico, etc. Pero supongo que se deslizará con esto para la mayoría de los idiomas de Europa occidental.
Tom McClure
1

Tenga en cuenta que incluir la lista de materiales UTF-8 no es necesariamente una buena idea: las versiones de Excel para Mac lo ignoran y en realidad mostrarán la lista de materiales como ASCII ... tres caracteres desagradables al comienzo del primer campo en su hoja de cálculo ...

Ned Martin
fuente
Sé que este comentario es 6 años después, pero FWIW: el uso de JavaScript para descargar un archivo '\uFEFF' + myCsvStringfunciona como se esperaba en Mac Excel 15.19.1 (2016).
bobjones
0

Verifique la codificación en la que está generando el archivo, para que Excel muestre el archivo correctamente, debe usar la página de códigos predeterminada del sistema.

¿Qué idioma estás usando? si es .Net solo necesita usar Encoding.Default mientras genera el archivo.

alberteína
fuente
Los datos de exportación son utf-8. Estoy escribiendo el archivo de exportación con php 5
Freddo411
Transcodifique los datos a la página de códigos de Windows-1252, no estoy seguro de cómo hacerlo con php
albertein
0

Si tiene código heredado en vb.net como yo, el siguiente código funcionó para mí:

    Response.Clear()
    Response.ClearHeaders()
    Response.ContentType = "text/csv"
    Response.Expires = 0
    Response.AddHeader("Content-Disposition", "attachment; filename=export.csv;")
    Using sw As StreamWriter = New StreamWriter(Context.Response.OutputStream, System.Text.Encoding.Unicode)
        sw.Write(csv)
        sw.Close()
    End Using
    Response.End()
Johann
fuente
0

He encontrado una manera de resolver el problema. Este es un truco desagradable pero funciona: abra el documento con Open Office , luego guárdelo en cualquier formato de Excel; el resultado .xlso .xlsxmostrará los caracteres acentuados.

Fred Reillier
fuente
1
El OP dice que está exportando mediante programación, por lo que no está buscando una solución que necesite intervención manual.
Christiaan Westerbeek
0

Con Ruby 1.8.7 codifico cada campo a UTF-16 y descarto BOM (tal vez).

El siguiente código se extrae de active_scaffold_export:

<%                                                                                                                                                                                                                                                                                                                           
      require 'fastercsv'                                                                                                                                                                                                                                                                                                        
      fcsv_options = {                                                                                                                                                                                                                                                                                                           
        :row_sep => "\n",                                                                                                                                                                                                                                                                                                        
        :col_sep => params[:delimiter],                                                                                                                                                                                                                                                                                          
        :force_quotes => @export_config.force_quotes,                                                                                                                                                                                                                                                                            
        :headers => @export_columns.collect { |column| format_export_column_header_name(column) }                                                                                                                                                                                                                                
      }                                                                                                                                                                                                                                                                                                                          

      data = FasterCSV.generate(fcsv_options) do |csv|                                                                                                                                                                                                                                                                           
        csv << fcsv_options[:headers] unless params[:skip_header] == 'true'                                                                                                                                                                                                                                                      
        @records.each do |record|                                                                                                                                                                                                                                                                                                
          csv << @export_columns.collect { |column|                                                                                                                                                                                                                                                                              
            # Convert to UTF-16 discarding the BOM, required for Excel (> 2003 ?)                                                                                                                                                                                                                                     
            Iconv.conv('UTF-16', 'UTF-8', get_export_column_value(record, column))[2..-1]                                                                                                                                                                                                                                        
          }                                                                                                                                                                                                                                                                                                                      
        end                                                                                                                                                                                                                                                                                                                      
      end                                                                                                                                                                                                                                                                                                                        
    -%><%= data -%>

La línea importante es:

Iconv.conv('UTF-16', 'UTF-8', get_export_column_value(record, column))[2..-1]
Antonio Bardazzi
fuente
-2

abra el archivo csv con notepad ++ haga clic en Codificar, seleccione convertir a UTF-8 (no convertir a UTF-8 (sin BOM)) Guardar abrir haciendo doble clic con Excel Espero que ayude a Christophe GRISON

Christophe GRISON
fuente
1
Esto no responde la pregunta, ya que se supone que debe hacerse mediante programación y no requiere la intervención del usuario para volver a guardar manualmente cada archivo
Joe W