Microsoft Excel обробляє діакритику у файлах .csv?


190

Я програмно експортую дані (за допомогою PHP 5.2) у тестовий файл .csv.
Приклад даних: Numéro 1(зверніть увагу на наголошене е). Дані є utf-8(без попереднього BOM).

Коли я відкриваю цей файл у MS Excel, відображається як Numéro 1.

Я можу відкрити це в текстовому редакторі (UltraEdit), який відображає його правильно. UE повідомляє, що персонаж є decimal 233.

Як я можу експортувати текстові дані у файл .csv, щоб MS Excel правильно візуалізував їх, бажано, не змушуючи використовувати майстра імпорту чи не за замовчуванням налаштувань майстра?


Мені б дуже цікаво почути більше про ваше рішення BOM, оскільки я вважаю, що я спробував "EF BB BF", який не працював для мене.
Джеймс Бейкер

3
Вибраним робочим рішенням було: * включити BOM; utf-8 * використовуйте цей заголовок: 'Тип вмісту: текст / звичайна; charset = utf-8 'Це "працювало" у програмі excel 2003 та excel 2007 - там, де працювали = відкрито без майстра імпорту та правильно надано діакритику. Я не перевірив, чи потрібна BOM.
Freddo411

2
BOM потрібен, я тільки зараз це перевірив. Без цього спеціальні символи не спрацьовують.
Олексій Кімініан

2
дуже сподобалося б, якби хтось міг сказати більше про те, як додати BOM (байт-маркер). Якщо я просто роблю щось на кшталт Response.Write (EF BB BF "), ці символи відображаються на початку файлу.
sydneyos

sydneyos: Як говорить Фергал нижче; Додайте \ uFEFF до рядка.
нооцит

Відповіді:


243

Правильно відформатований файл UTF8 може мати позначку порядку байт як його перші три октети. Це шістнадцяткові значення 0xEF, 0xBB, 0xBF. Ці октети служать для позначення файлу як UTF8 (оскільки вони не стосуються інформації "порядку байтів"). 1 Якщо цього BOM не існує, споживачеві / читачу залишається зробити висновок про тип кодування тексту. Читачі, які не здатні UTF8, будуть читати байти як деякі інші кодування, такі як Windows-1252, і відображати символи на початку файлу.

Відома помилка, де Excel, відкриваючи файли CSV UTF8 через об'єднання файлів, припускає, що вони знаходяться в однобайтовому кодуванні, не враховуючи присутність BOM UTF8. Це не може бути виправлено будь-якою кодовою сторінкою системи або мовою за замовчуванням. BOM не підкаже в Excel - він просто не працюватиме. (Звіт про меншину стверджує, що BOM іноді спрацьовує майстра "Імпортувати текст".) Здається, ця помилка існує в Excel 2003 і раніше. Більшість звітів (серед відповідей тут) кажуть, що це виправлено у програмі Excel 2007 та новіших версіях.

Зауважте, що ви завжди можете * правильно відкрити файли CSV UTF8 в Excel за допомогою майстра "Імпорт тексту", який дозволяє вказати кодування файлу, який ви відкриваєте. Звичайно, це набагато менш зручно.

Читачі цієї відповіді, швидше за все, знаходяться в ситуації, коли вони особливо не підтримують Excel <2007, але надсилають необроблений текст UTF8 в Excel, який неправильно трактує його та посипає ваш текст Ãта іншими подібними символами Windows-1252. Додавання UTF8 BOM - це, мабуть, найкращий та швидкий виправлення.

Якщо ви застрягли з користувачами на старих програмах Excels, а Excel є єдиним споживачем ваших CSV-файлів, ви можете обійти це шляхом експорту UTF16 замість UTF8. Excel 2000 та 2003 подвійно натискатимуть їх правильно. (Деякі інші текстові редактори можуть мати проблеми з UTF16, тому вам, можливо, доведеться уважно зважити свої параметри.)


* За винятком випадків, коли ви не можете, (принаймні) майстер імпорту Mac Excel 2011 не завжди працює з усіма кодуваннями, незалежно від того, що ви їм сказали. </anecdotal-evidence> :)


14
Мене вічно взяли, щоб знайти, де вказати кодування. Зберегти діалогове вікно> Кнопка інструментів> Веб-параметри> Вкладка кодування. Вони впевнені, що приховують такі важливі речі.
Трайнко

6
Неправильно: додавання BOM до файлу UTF-8 завантажує цей файл правильно, не вимагаючи майстра імпорту в Excel 2007.
Віктор Ніколлет,

3
Ми знайшли те саме, що сьогодні говорить Віктор (використовуючи Excel 2010, це все, що ми мали в наявності). Додавання UTF-8 BOM / Підпис (EF BB BF), здавалося, виправляє подвійне клацання за допомогою кодування системи за замовчуванням, і правильно використовує UTF8 :)
Danny Tuppeny

20
Загалом , у файлі, кодованому UTF-8, не повинно бути попередньо встановленим позначенням байтів. UTF-8 не має змінного порядку байтів, і розміщення його там саботує сумісність з UCF-8 ASCII. Існують деякі конкретні формати файлів, які дозволяють або заохочують UTF-8 faux-BOM, але в іншому випадку цього слід уникати. CSV повністю невідомий кодування, тому хтось здогадається, чи буде даний інструмент інтерпретувати послідовність байтів 0xEF 0xBB 0xBF як показник UTF-8; невидимий керуючий символ у першій комірці; символи в першій комірці; або щось інше цілком.
bobince

3
@Ian: Ніхто точно не знає, що UTF-8 з BOM також - 0xEF 0xBB 0xBF - це дійсна послідовність і в більшості застарілих кодувань (отже, її часто неправильно трактують як ISO-8859-1 або cp1252 і відображають як ). Це допомагає лише алгоритмам відгадування, і для форматів файлів, які спеціально враховують його (наприклад, XML). Недоліком включення faux-BOM у файли UTF-8 є те, що ви порушите їх сумісність з ASCII (головна точка продажу для UTF-8). Багато текстових інструментів, що не знають кодування, зірвуться зіткнувшись з несподіваним провідним faux-BOM.
bobince

39

Передбачаючи, що BOM (\ uFEFF) працював на мене (Excel 2007), в цьому Excel розпізнав файл як UTF-8. В іншому випадку збереження та використання майстра імпорту працює, але менш ідеально.


1
Він все ще відкриває майстра імпорту тексту, тому різниця полягає в тому, що ви можете просто подвійним клацанням миші, тому все ще не ідеальне, але єдине відоме рішення у будь-якому разі.
haridsv

Для мене не з'являється майстер імпорту з програмою Excel 2007.
Віктор Ніколлет,

Для мене також немає майстра імпорту - він працює, як очікувалося, якщо присутній UTF8 BOM / Підпис (EF BB BF).
Danny Tuppeny

Крім того, \ufeffчи є BOM UTF-16 (BE), а не BOM UTF-8
Alastair McCormack

2
Ні, @AlastairMcCormack, це або, залежно від того, як це закодовано. "\ ufeff", кодований як UTF-8, є саме EF BB BF. (Зашифровано як UTF-16, це буде лише два байти.)
Дейв Берт

30

Нижче наведено PHP-код, який я використовую в своєму проекті, коли надсилаю Microsoft Excel користувачеві:

  /**
   * 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;
  }

ОНОВЛЕНО: Вдосконалення імені файлу та виправлення виправленої довжини BUG виправлення. Завдяки TRiG та @ ivanhoe011


1
Я спробував кілька інших пропозицій на цій сторінці, але це спрацювало для мене в Excel 2007. Найважливішими змінами було використання вкладок замість коми (хоча це файл .csv) та рядок вище, що повторює два символи, за якими слід виклик на mb_convert_encoding (). Я також повинен був перекомпілювати PHP з --enable-mbstring, щоб отримати підтримку mb_convert_encoding (). Дякую!
Рассел G

1
Це добре працювало і для мене, дякую. Однак у Safari я отримую помилку в консолі "Ресурс інтерпретується як документ, але передається як ..." Я думаю, що це вигадка WebKit, судячи з stackoverflow.com/questions/3899426/… , але, можливо, це не та / або хтось має знайшов рішення. Крім того, у вашому прикладі я б запропонував зміни: 'Content-Disposition: attachment; filename="'.$filename.'.csv"'оскільки Firefox хоче подвійні лапки, інакше він відріже ваше ім'я після пробілу.
Казимир

Чому ви виводите CSV ( text/csv), але називаєте його Excel ( application/vnd.ms-excel)?
TRiG

2
Це чудово працює! Я можу підтвердити, що він працює і на Mac (в Office 2011).
Джонатан

Чи не повинно бути цього header('Content-Length: '. mb_strlen($encoded_csv, 'UTF-16LE'));?
Річ Бредшоу

13

Відповідь на всі комбінації версій Excel (2003 + 2007) та типів файлів

Більшість інших відповідей тут стосуються лише їх версії Excel і не обов'язково допоможуть вам, оскільки їх відповідь може бути неправдивою для вашої версії Excel.

Наприклад, додавання символу BOM створює проблеми з автоматичним розпізнаванням стовпців, але не з кожною версією Excel.

Існує 3 змінні, які визначають, чи працює він у більшості версій Excel:

  • Кодування
  • Наявність персонажа BOM
  • Роздільник клітин

Хтось стоїк в SAP спробував кожну комбінацію та повідомив про результат. Кінцевий результат? Використовуйте UTF16le з символом BOM та вкладкою як роздільник, щоб він працював у більшості версій Excel.

Ви мені не вірите? Я не хотів би, але читайте тут і плачу: http://wiki.sdn.sap.com/wiki/display/ABAP/CSV+tests+of+encoding+and+column+separator


Чому б просто не додати sep=,або все, що ви хочете використовувати? Якщо ви вже додаєте BOM, я вважаю, що ви не проти додавати матеріали до файлу.
Кейсі

Ну, насправді, щоб відповісти на моє власне запитання, ви не додавали б декларацію роздільника поля, оскільки це призводить до припинення роботи цього фокусу. Таким чином, це кодована скрипка або ваш файл не інтерпретується належним чином як CSV, якщо ваші користувачі мають неправильні налаштування регіону.
Кейсі

1
utf-16le + BOM (0xFF 0xFE) + вкладка найкраща
zhaozhi

10

виберіть UTF-8, що складається під час імпорту. якщо ви використовуєте Office 2007, саме тут ви вибрали його: відразу після відкриття файлу.


1
Це корисно. Я змінив питання, щоб запитати, як це зробити, не звертаючись до майстра
Freddo411,

9

Echo UTF-8 BOM перед виведенням даних CSV. Це виправляє всі проблеми з персонажами в Windows, але не працює для Mac.

echo "\xEF\xBB\xBF";

Це працює для мене, тому що мені потрібно генерувати файл, який буде використовуватися лише на ПК з Windows.


Не вірно для кожного типу роздільника стовпців, ані для кожної версії Excel. Прочитайте мою відповідь нижче (зараз на даний момент).
Крістіан Вестербек

7

UTF-8 не працює для мене в офісі 2007 без будь-якого пакета обслуговування, з або без BOM (U + ffef або 0xEF, 0xBB, 0xBF, ні працює), встановлення sp3 змушує UTF-8 працювати, коли 0xEF, 0xBB, 0xBF BOM є заздалегідь.

UTF-16 працює при кодуванні в python, використовуючи "utf-16-le" з попередньою BOM 0xff 0xef і використовуючи вкладку в якості сепаратора. Мені довелося вручну виписати BOM, а потім скористатися "utf-16-le", а не "utf-16", інакше кожний encode () передував BOM до кожного записаного рядка, який з'явився як сміття в першому стовпчику другий рядок і після.

не можу сказати, чи буде UTF-16 працювати без встановленого спр., тому що я не можу повернутися назад. зітхати

Це на windows, не знаю про офіс для MAC.

для обох робочих випадків імпорт працює при запуску завантаження безпосередньо з браузера, а майстер імпорту тексту не втручається, він працює так, як ви очікували.


Працює в Excel 2011 і для Mac.
Адам

дякую за ваш пост, використовувати utf-16le нормально, навіть коли ви не встановлювали Office 2007 sp3, але BOM має бути 0xFF 0xFE
zhaozhi

4

Як сказав Фрегал, \ uFEFF - це шлях.

<%@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
%>

1
Просто дивіться і дивіться, як ваш роздільник вкладок ігнорується в Excel 2007, коли ви використовуєте BOM. Ви повинні придумати щось більше.
Крістіан Вестербек

3

Я також помітив, що на це питання "відповіли" деякий час тому, але я не розумію історії, в якій говориться, що ви не можете успішно відкрити файл csv, закодований utf8, в Excel, не використовуючи текстового майстра.

Мій досвід відтворення: Введіть Old MacDonald had a farm,ÈÌÉÍØу Блокнот, натисніть Enter, а потім Зберегти як (використовуючи опцію UTF-8).

Використання Python для показу того, що насправді є:

>>> 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

Добре. Блокнот поставив BOM спереду.

Тепер увійдіть у Провідник Windows, двічі клацніть ім'я файлу або клацніть правою кнопкою миші та скористайтеся пунктом "Відкрити за допомогою ..." та спливає Excel (2003) із відображенням, як очікувалося.


@Cocowalla: Ну, я просто спробував це (знову ж таки, я протестував це перед публікацією), і він працював з Excel 2007 (що я зараз використовую). Ви зробили, open('oldmac.csv', 'rb').read()щоб перевірити свій внесок?
Джон Махін

Я не намагався з Excel 2007 (я знаю, що Excel 2007 добре читає файли UTF-8 з BOM), я намагався з Excel 2003
Cocowalla

@Cocowalla: Добре це працювало для мене з Excel 2003, коли я мав це. Ви впевнені, що у вас є останній пакет послуг для Excel 2003? Ви перевірили свій внесок, як я запропонував?
Джон Махін

Я перевірив, що блокнот застряг у BOM на початку файлу, але я перебуваю на Excel 2003 SP2 (доступний SP3) - тому, мабуть, це працює лише в SP3
Cocowalla

2

Ви можете зберегти файл HTML з розширенням 'xls', і наголоси спрацюють (принаймні до 2007 року).

Приклад: збережіть це (використовуючи Save As utf8 у Блокноті) як 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>

цікавий варіант. Він відкриває текст правильно, але чомусь вся сторінка повністю біла. Без класичних ліній електронних таблиць, що розмежовують рядки та стовпці (офіс для mac)
Себастьян Састре

Так, те саме в Office 2007 для Windows. Мене завжди дивувало, що це взагалі спрацювало, якщо чесно. (Зверніть увагу, якщо ви додасте border="1"до столу, ви робите отримуєте лінії, але тільки навколо 4 клітини :)
Benjol

1

Це лише питання кодування символів. Схоже, ви експортуєте свої дані як UTF-8: у UTF-8 є двобайтова послідовність 0xC3 0xA9, яка при інтерпретації в Windows-1252 є ©. Коли ви імпортуєте свої дані в Excel, не забудьте сказати, що кодування символів, яке ви використовуєте, є UTF-8.


Я підтвердив, що дані є UTF-8. Що я вкладаю у файл, щоб переконатися, що мої дані є utf-8 (BOM?)
Freddo411,

Я думаю, що вам потрібно змінити кодування файлу, excel використовує кодову сторінку за замовчуванням для системи для обробки файлів CSV
albertein

Я не зовсім впевнений, оскільки у мене не встановлений Excel на машині, яку я зараз використовую, але з OpenOffice є спадне поле для кодування символів при імпорті CSV-файлу. Звідти виберіть Unicode (UTF-8).
Адам Розенфілд

У Excel немає випадаючого AFAIK
Альбертейн

1

Формат CSV реалізований як ASCII, а не unicode, в Excel, таким чином керуючи діакритикою. У нас виникла та сама проблема, як я зрозумів, що офіційний стандарт CSV був визначений як ASCII на базі Excel.


Власне, CSV не пов'язаний з конкретним кодуванням. Це Excel, який передбачає ASCII. en.wikipedia.org/wiki/Comma-separated_values
spoulson

Це те, що я сказав. "реалізовано як ASCII в Excel", "CSV визначений як ASCII на основі Excel". Не впевнений, що ти робиш, коли, здається, зі мною згоден.
Jeff Yates

2
Насправді ви говорите: "Формат CSV реалізований як ASCI", я думаю, саме звідси випливає плутанина.
РічардОД

1

Excel 2007 належним чином читає UTF-8 з кодуванням BOM (EF BB BF).

Excel 2003 (а може і раніше) читає UTF-16LE з BOM (FF FE), але з таблицями TAB замість коми або крапки з комою.


1

Я можу змусити CSV правильно розібратися в Excel 2007 як розділений табуляцією маленький ендіанський UTF-16, починаючи з належного позначення порядку байтів.


1

Запис BOM у вихідний CSV-файл насправді працював для мене у 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

Для отримання додаткової інформації http://crashcoursing.blogspot.com/2011/05/exporting-csv-with-special-characters.html Дякую, хлопці!


Так, це працювало для мене з Excel 2010. У використанні Java printWriter.print('\ufeff')див. Також Як додати BT UTF-8 у Java .
tsauerwein

1

Ще одне знайдене нами рішення - це просто кодувати результат у вигляді коду Windows Page 1252 (Windows-1252 або CP1252). Це можна зробити, наприклад, встановивши Content-Typeвідповідним чином щось подібне text/csv; charset=Windows-1252та встановивши кодування символів потоку відповідей аналогічно.


Дякую за це. Працює на windows windows та mac. Я цим користуюся.
Себастьян Састре

Це спрацює лише в тому випадку, якщо діапазон символів, що не належать ascii, повністю підпадає під Windows-1252. Так, наприклад, немає корейської / китайської / японської мови, жодної кирилиці тощо. Але я думаю, що ви просунетеся з цим для більшості західних європейських мов.
Том МакКлур

1

Зауважте, що включення UTF-8 BOM не обов'язково є гарною ідеєю - версії Mac Excel ігнорують його і фактично відображатимуть BOM як ASCII ... три неприємні символи на початку першого поля вашої електронної таблиці ...


Я знаю, що цей коментар через 6 років, але FWIW: Використання JavaScript для завантаження файлу, як '\uFEFF' + myCsvStringпрацює, як очікувалося в Mac Excel 15.19.1 (2016).
bobjones

0

Перевірте кодування, в якому ви генеруєте файл, щоб Excel відображав файл правильно, ви повинні використовувати системну кодову сторінку за замовчуванням.

Якою мовою ви користуєтесь? якщо це .Net, вам потрібно використовувати лише Encoding.Default під час генерації файлу.


Дані про експорт - utf-8. Я пишу експортний файл із php 5
Freddo411

Перекодируйте дані на кодову сторінку Windows-1252, я не впевнений, як це дозволити за допомогою php
albertein

0

Якщо у вас є застарілий код на vb.net, як у мене, наступний код працював для мене:

    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()

0

Я знайшов спосіб вирішити проблему. Це неприємний злом, але він працює: відкрийте документ з Open Office , а потім збережіть його у будь-якому форматі Excel; отримані .xlsабо .xlsxвідображатимуться наголошені символи.


1
ОП каже, що він експортує програмно, тому не шукає рішення, яке потребує ручного втручання.
Крістіан Вестербек

0

За допомогою Ruby 1.8.7 я кодую кожне поле до UTF-16 і відкидаю BOM (можливо).

Наступний код витягується з 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 -%>

Важливий напрямок:

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

-2

відкрийте файл csv із блокнотом ++ клацанням на Encode, виберіть конвертувати в UTF-8 (не конвертувати в UTF-8 (без BOM)).


1
Це не відповідає на питання, оскільки його слід робити програмно і не вимагати втручання користувача, щоб вручну відновити кожен файл
Joe W
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.