Ответ 1
Как @jlarson обновил информацию о том, что Mac был самым большим виновником, мы могли бы получить еще немного. Office для Mac имеет, по крайней мере, 2011 год и обратно, довольно плохую поддержку для чтения форматов Unicode при импорте файлов.
Поддержка UTF-8, по-видимому, близка к несуществующей, прочитала небольшое количество комментариев о ее работе, в то время как большинство утверждают, что это не так. К сожалению, у меня нет Mac для тестирования. Итак, снова: сами файлы должны быть в порядке, как UTF-8, но импорт останавливает процесс.
Написал быстрый тест в Javascript для экспорта процентов экранированного UTF-16 маленького и большого endian, с/без спецификации и т.д.
Код, вероятно, должен быть рефакторирован, но должен быть в порядке для тестирования. Это может работать лучше, чем UTF-8. Конечно, это также обычно означает большие передачи данных, так как любой глиф представляет собой два или четыре байта.
Здесь вы можете найти скрипку:
Обратите внимание, что он не обрабатывает CSV каким-либо определенным образом. Он в основном предназначен для чистого преобразования в URL-адрес данных, имеющий UTF-8, UTF-16 большой/маленький конец и +/- спецификацию. В скрипте есть один вариант заменить запятые на вкладки, но верьте, что это будет довольно хакерское и хрупкое решение, если оно работает.
Обычно используется как:
// Initiate
encoder = new DataEnc({
mime : 'text/csv',
charset: 'UTF-16BE',
bom : true
});
// Convert data to percent escaped text
encoder.enc(data);
// Get result
var result = encoder.pay();
Существует два свойства результата объекта:
1.) encoder.lead
Это тип mime-типа, charset и т.д. для URL-адреса данных. Построен из параметров, переданных в инициализатор, или можно также сказать .config({ ... new conf ...}).intro()
для повторной сборки.
data:[<MIME-type>][;charset=<encoding>][;base64]
Вы можете указать base64, но нет преобразования base64 (по крайней мере, не так далеко).
2.) encoder.buf
Это строка с процентом экранированных данных.
Функция .pay()
просто возвращает 1.) и 2.) как один.
Основной код:
function DataEnc(a) {
this.config(a);
this.intro();
}
/*
* http://www.iana.org/assignments/character-sets/character-sets.xhtml
* */
DataEnc._enctype = {
u8 : ['u8', 'utf8'],
// RFC-2781, Big endian should be presumed if none given
u16be : ['u16', 'u16be', 'utf16', 'utf16be', 'ucs2', 'ucs2be'],
u16le : ['u16le', 'utf16le', 'ucs2le']
};
DataEnc._BOM = {
'none' : '',
'UTF-8' : '%ef%bb%bf', // Discouraged
'UTF-16BE' : '%fe%ff',
'UTF-16LE' : '%ff%fe'
};
DataEnc.prototype = {
// Basic setup
config : function(a) {
var opt = {
charset: 'u8',
mime : 'text/csv',
base64 : 0,
bom : 0
};
a = a || {};
this.charset = typeof a.charset !== 'undefined' ?
a.charset : opt.charset;
this.base64 = typeof a.base64 !== 'undefined' ? a.base64 : opt.base64;
this.mime = typeof a.mime !== 'undefined' ? a.mime : opt.mime;
this.bom = typeof a.bom !== 'undefined' ? a.bom : opt.bom;
this.enc = this.utf8;
this.buf = '';
this.lead = '';
return this;
},
// Create lead based on config
// data:[<MIME-type>][;charset=<encoding>][;base64],<data>
intro : function() {
var
g = [],
c = this.charset || '',
b = 'none'
;
if (this.mime && this.mime !== '')
g.push(this.mime);
if (c !== '') {
c = c.replace(/[-\s]/g, '').toLowerCase();
if (DataEnc._enctype.u8.indexOf(c) > -1) {
c = 'UTF-8';
if (this.bom)
b = c;
this.enc = this.utf8;
} else if (DataEnc._enctype.u16be.indexOf(c) > -1) {
c = 'UTF-16BE';
if (this.bom)
b = c;
this.enc = this.utf16be;
} else if (DataEnc._enctype.u16le.indexOf(c) > -1) {
c = 'UTF-16LE';
if (this.bom)
b = c;
this.enc = this.utf16le;
} else {
if (c === 'copy')
c = '';
this.enc = this.copy;
}
}
if (c !== '')
g.push('charset=' + c);
if (this.base64)
g.push('base64');
this.lead = 'data:' + g.join(';') + ',' + DataEnc._BOM[b];
return this;
},
// Deliver
pay : function() {
return this.lead + this.buf;
},
// UTF-16BE
utf16be : function(t) { // U+0500 => %05%00
var i, c, buf = [];
for (i = 0; i < t.length; ++i) {
if ((c = t.charCodeAt(i)) > 0xff) {
buf.push(('00' + (c >> 0x08).toString(16)).substr(-2));
buf.push(('00' + (c & 0xff).toString(16)).substr(-2));
} else {
buf.push('00');
buf.push(('00' + (c & 0xff).toString(16)).substr(-2));
}
}
this.buf += '%' + buf.join('%');
// Note the hex array is returned, not string with '%'
// Might be useful if one want to loop over the data.
return buf;
},
// UTF-16LE
utf16le : function(t) { // U+0500 => %00%05
var i, c, buf = [];
for (i = 0; i < t.length; ++i) {
if ((c = t.charCodeAt(i)) > 0xff) {
buf.push(('00' + (c & 0xff).toString(16)).substr(-2));
buf.push(('00' + (c >> 0x08).toString(16)).substr(-2));
} else {
buf.push(('00' + (c & 0xff).toString(16)).substr(-2));
buf.push('00');
}
}
this.buf += '%' + buf.join('%');
// Note the hex array is returned, not string with '%'
// Might be useful if one want to loop over the data.
return buf;
},
// UTF-8
utf8 : function(t) {
this.buf += encodeURIComponent(t);
return this;
},
// Direct copy
copy : function(t) {
this.buf += t;
return this;
}
};
Предыдущий ответ:
У меня нет настроек для репликации, но если ваш случай совпадает с @jlarson, то полученный файл должен быть правильным.
Этот ответ стал несколько длинным (интересная тема, которую вы говорите?), но обсудите различные аспекты вокруг вопроса, что (вероятно) происходит, и как на самом деле проверить, что происходит по-разному.
TL; DR:
Текст, скорее всего, импортируется как ISO-8859-1, Windows-1252 и т.п., а не как UTF-8. Принудительное приложение для чтения файла как UTF-8 с помощью импорта или других средств.
PS: UniSearcher - это хороший инструмент, доступный в этом путешествии.
Длинный путь
"Самый простой" способ быть на 100% уверенным, что мы смотрим, - это использовать hex-редактор для результата. В качестве альтернативы используйте hexdump
, xxd
или тому подобное из командной строки для просмотра файла. В этом случае последовательность байтов должна быть последовательностью байтов UTF-8, поставляемой из script.
В качестве примера, если мы берем script jlarson, он принимает массив data
:
data = ['name', 'city', 'state'],
['\u0500\u05E1\u0E01\u1054', 'seattle', 'washington']
Этот объединяется в строку:
name,city,state<newline>
\u0500\u05E1\u0E01\u1054,seattle,washington<newline>
который преобразует Unicode в:
name,city,state<newline>
Ԁסกၔ,seattle,washington<newline>
Поскольку UTF-8 использует ASCII в качестве базы (байты с самым высоким битом не установлены, те же, что и в ASCII), единственная специальная последовательность в тестовых данных - "Ԁ ס ก ၔ", которая, в свою очередь, равна:
Code-point Glyph UTF-8
----------------------------
U+0500 Ԁ d4 80
U+05E1 ס d7 a1
U+0E01 ก e0 b8 81
U+1054 ၔ e1 81 94
Глядя на шестнадцатеричный дамп загруженного файла:
0000000: 6e61 6d65 2c63 6974 792c 7374 6174 650a name,city,state.
0000010: d480 d7a1 e0b8 81e1 8194 2c73 6561 7474 ..........,seatt
0000020: 6c65 2c77 6173 6869 6e67 746f 6e0a le,washington.
Во второй строке мы найдем d480 d7a1 e0b8 81e1 8194
, которые совпадают с приведенным выше:
0000010: d480 d7a1 e0b8 81 e1 8194 2c73 6561 7474 ..........,seatt
| | | | | | | | | | | | | |
+-+-+ +-+-+ +--+--+ +--+--+ | | | | | |
| | | | | | | | | |
Ԁ ס ก ၔ , s e a t t
Ни один из других символов не искажен.
Выполняйте аналогичные тесты, если хотите. Результат должен быть аналогичным.
По предоставленному образцу â€", â€, “
Мы также можем взглянуть на образец, предоставленный в вопросе. Вероятно, предполагается, что текст представлен в Excel/TextEdit по кодовой странице 1252.
Процитировать Wikipedia на Windows-1252:
Windows-1252 или CP-1252 - это кодировка символов латинского алфавита, используемая по умолчанию в устаревших компонентах Microsoft Windows на английском и некоторых других Западные языки. Это одна из версий в группе кодовых страниц Windows. В пакетах LaTeX он упоминается как "ansinew".
Получение исходных байтов
Чтобы перевести его обратно в оригинальную форму, мы можем посмотреть макет кодовой страницы, из которого получим:
Character: <â> <€> <"> <,> < > <â> <€> < > <,> < > <â> <€> <œ>
U.Hex : e2 20ac 201d 2c 20 e2 20ac 9d 2c 20 e2 20ac 153
T.Hex : e2 80 94 2c 20 e2 80 9d* 2c 20 e2 80 9c
-
U
не подходит для Unicode -
T
не подходит для Translated
Например:
â => Unicode 0xe2 => CP-1252 0xe2
" => Unicode 0x201d => CP-1252 0x94
€ => Unicode 0x20ac => CP-1252 0x80
Специальные случаи, такие как 9d
, не имеют соответствующей кодовой точки в CP-1252, они просто копируются напрямую.
Примечание. Если вы посмотрите на искаженную строку, скопировав текст в файл и сделав шестнадцатеричный дамп, сохраните файл, например, кодировку UTF-16, чтобы получить значения Unicode, представленные в таблице. Например. в Vim:
set fenc=utf-16
# Or
set fenc=ucs-2
Байты для UTF-8
Затем мы объединяем результат, строку T.Hex
, в UTF-8. В последовательностях UTF-8 байты представлены ведущим байтом, сообщающим нам, сколько последующих байтов делает глиф. Например, если у байта есть двоичное значение 110x xxxx
, мы знаем, что этот байт и следующий представляют одну кодовую точку. Всего два. 1110 xxxx
говорит нам, что это три и так далее. Значения ASCII не имеют большого битового набора, поэтому любое сопоставление байтов 0xxx xxxx
является автономным. Всего один байт.
0xe2 = 1110 0010bin => 3 bytes => 0xe28094 (em-dash) — 0x2c = 0010 1100bin => 1 byte => 0x2c (comma) , 0x2c = 0010 0000bin => 1 byte => 0x20 (space) 0xe2 = 1110 0010bin => 3 bytes => 0xe2809d (right-dq) " 0x2c = 0010 1100bin => 1 byte => 0x2c (comma) , 0x2c = 0010 0000bin => 1 byte => 0x20 (space) 0xe2 = 1110 0010bin => 3 bytes => 0xe2809c (left-dq) "
Вывод; Оригинальная строка UTF-8:
—, ", "
Переплетение обратно
Мы также можем сделать обратное. Исходная строка в виде байтов:
UTF-8: e2 80 94 2c 20 e2 80 9d 2c 20 e2 80 9c
Соответствующие значения в cp-1252:
e2 => â
80 => €
94 => "
2c => ,
20 => <space>
...
и т.д., результат:
â€", â€, “
Импорт в MS Excel
Другими словами: проблема заключается в том, как импортировать текстовые файлы UTF-8 в MS Excel и некоторые другие приложения. В Excel это можно сделать по-разному.
- Метод один:
Не сохраняйте файл с расширением, распознанным приложением, например .csv
или .txt
, но опустите его полностью или сделайте что-нибудь.
В качестве примера сохраните файл как "testfile"
, без расширения. Затем в Excel откройте файл, подтвердите, что мы действительно хотим открыть этот файл, а voilà мы получаем с опцией кодирования. Выберите UTF-8, и файл должен быть правильно прочитан.
- Метод второй:
Используйте данные импорта вместо открытого файла. Что-то вроде:
Data -> Import External Data -> Import Data
Выберите кодировку и продолжите.
Убедитесь, что Excel и выбранный шрифт действительно поддерживают глиф
Мы также можем протестировать поддержку шрифтов для символов Юникода, используя, иногда, более дружественный буфер обмена. Например, скопируйте текст с этой страницы в Excel:
Если существует поддержка кодовых точек, текст должен выглядеть отлично.
Linux
В Linux, который является, прежде всего, UTF-8 в userland, это не должно быть проблемой. Используя Libre Office Calc, Vim и т.д., Отобразите файлы правильно.
Почему он работает (или должен)
encodeURI из состояний spec (также читайте sec-15.1.3):
Функция encodeURI вычисляет новую версию URI, в которой каждый экземпляр определенных символов заменяется одной, двумя, тремя или четырьмя управляющими последовательностями, представляющими кодировку UTF-8 символа.
Мы можем просто протестировать это в нашей консоли, например:
>> encodeURI('Ԁסกၔ,seattle,washington')
<< "%D4%80%D7%A1%E0%B8%81%E1%81%94,seattle,washington"
Когда мы регистрируем escape-последовательности, равные единицам в шестнадцатеричном дампе выше:
%D4%80%D7%A1%E0%B8%81%E1%81%94 (encodeURI in log)
d4 80 d7 a1 e0 b8 81 e1 81 94 (hex-dump of file)
или, проверяя 4-байтовый код:
>> encodeURI('')
<< "%F3%B1%80%81"
Если это не соответствует
Если ничего из этого не применимо, это может помочь, если вы добавили
- Пример ожидаемого ввода и искаженного вывода (копирование).
- Пример hex-дампа исходных данных и файла результата.