Преобразование строк ISO-8859-1 в UTF-8 в C / C ++

18

Вы считаете, что это будет легко доступно, но мне сложно найти простую библиотечную функцию, которая преобразует строку C или C ++ из ISO-8859-1 в UTF-8. Я читаю данные, которые находятся в 8-битной кодировке ISO-8859-1, но нужно преобразовать ее в строку UTF-8 для использования в базе данных SQLite и, в конечном итоге, в приложении для Android.

Я нашел один коммерческий продукт, но в настоящее время он выходит за рамки моего бюджета.

    
задан gordonwd 30.10.2010 в 19:17
источник
  • В этом нет ничего простого. Вы можете использовать библиотеку ICU с открытым исходным кодом. –  Hans Passant 30.10.2010 в 19:23
  • Если вам нужно это сделать, то самым простым кодом является предварительная генерация таблицы из 128 (или около того) символов UTF-8, соответствующих символам 8859-1 с установленным верхним битом. Остальные 128 8859-1 символов не изменены. Таким образом, ваш код не должен понимать Unicode вообще. Также обратите внимание на разницу между ISO-8859-1 и Windows CP-1252. У последнего есть некоторые дополнительные символы, в которых у 8859-1 есть пробелы (неиспользованные кодовые точки). Если вы не собираетесь проверять, что ваш ввод действительно ISO-8859-1, нет смысла не принимать CP-1252, потому что вы увидите его неправильно обозначенным. –  Steve Jessop 30.10.2010 в 19:30
  • @Steve: поскольку UTF-8 является переменной длиной (в данном случае 1 или 2 байта на символ), таблица поиска не так проста в использовании. См. Мой ответ, который должен быть таким же быстрым и намного более простым. –  R.. 30.10.2010 в 19:54
  • @R .: ну, «легкий» - относительный термин. stpcpy помогает, если вы такой программист, который хорош с размерами буфера. –  Steve Jessop 30.10.2010 в 20:48
  • stpcpy (даже если он стандартен или теперь стал стандартным сейчас??) является helluvalot накладных расходов для 1- и 2-байтных копий. Вам лучше было бы всегда копировать 2 байта (вручную) и включать в себя некоторый код, чтобы пропустить второй указатель вперед, если байт скопирован равным 0 (который почти наверняка может быть ветром). –  R.. 31.10.2010 в 17:48

6 ответов

32

Если ваша исходная кодировка будет всегда соответствовать ISO-8859-1, это тривиально. Вот цикл:

unsigned char *in, *out;
while (*in)
    if (*in<128) *out++=*in++;
    else *out++=0xc2+(*in>0xbf), *out++=(*in++&0x3f)+0x80;

Для обеспечения безопасности вам необходимо убедиться, что выходной буфер в два раза больше, чем входной буфер, или же включить ограничение по размеру и проверить его в состоянии цикла.

    
ответ дан R.. 30.10.2010 в 19:53
источник
  • Ничего себе. Это очень полезно! Я не ожидал еще одного алгоритма поиска таблицы. Теперь для ANSEL-to-UTF-8 ... –  gordonwd 30.10.2010 в 20:31
  • Это, безусловно, отвечает на вопрос. Но, как я уже сказал в комментарии выше, люди отправят вам CP-1252, помеченную как ISO-8859-1. Веб-сервер является примером того, что я сработал, что уговорило меня о проблеме, но также текстовые редакторы, которые утверждают, что сохраняют их как «Latin-1», когда они не являются. То, что «если ваша исходная кодировка всегда будет ISO-8859-1», является довольно большой «если», и может быть трудно отследить и устранить злоумышленника. –  Steve Jessop 30.10.2010 в 20:46
  • @Steve: вы могли бы добавить else if (* in <192) goto error; случай с ошибкой при столкновении с любыми управляющими кодами ISO-8859-1 (которые, возможно, неправильно кодируются персонажами Windows-1252, а не полезные символы). –  R.. 31.10.2010 в 02:36
  • @gordon: Я не знаком с ANSEL, но вы должны знать, что ISO-8859-1 - единственная устаревшая кодировка, которую легко конвертировать в UTF-8. Все остальное потребует таблицы поиска. Стив сказал, что мой «Если ..» большой, если. –  R.. 31.10.2010 в 02:37
  • Это довольно плохо написанный код с точки зрения ремонтопригодности. Используйте больше брекетов. –  syb0rg 04.02.2014 в 01:18
Показать остальные комментарии
7

В c ++ я использую это:

std::string iso_8859_1_to_utf8(std::string &str)
{
    string strOut;
    for (std::string::iterator it = str.begin(); it != str.end(); ++it)
    {
        uint8_t ch = *it;
        if (ch < 0x80) {
            strOut.push_back(ch);
        }
        else {
            strOut.push_back(0xc0 | ch >> 6);
            strOut.push_back(0x80 | (ch & 0x3f));
        }
    }
    return strOut;
}
    
ответ дан Lord Raiden 05.10.2016 в 23:37
источник
3

Стандарт C ++ 03 не предоставляет функции прямого преобразования между конкретными кодировками.

В зависимости от вашей ОС вы можете использовать iconv () для Linux, MultiByteToWideChar () и amp; Co. в Windows. Библиотека, которая обеспечивает большую поддержку преобразования строк, - это библиотека ICU, которая является открытым исходным кодом.

    
ответ дан cytrinox 30.10.2010 в 19:29
источник
  • > «Стандарт C ++ не предоставляет функции прямого преобразования между кодировками –  Cheers and hth. - Alf 24.01.2018 в 13:34
2

У пользователей Unicode есть таблицы, которые могут помочь, если они столкнулись с Windows 1252 вместо истинного ISO-8859-1. Окончательный вариант выглядит как этот , который отображает каждую точку кода в CP1252 кодовая точка в Юникоде. Кодирование Unicode как UTF-8 - это простое упражнение.

Нетрудно разобрать эту таблицу непосредственно и сформировать из нее во время компиляции таблицу поиска.

    
ответ дан RBerteig 31.10.2010 в 02:44
источник
0

ISO-8859-1 для UTF-8 включает в себя не что иное, как алгоритм кодирования, поскольку ISO-8859-1 является подмножеством Unicode. Таким образом, у вас уже есть коды кода Unicode. Проверьте Википедию для алгоритма.

С ++ аспекты - интеграция с iostreams - намного сложнее.

Я предлагаю вам прогуляться по этой горе, вместо того, чтобы пытаться просверлить ее или залезть на нее, то есть реализовать простую строку в строковый конвертер.

Cheers & amp; НТН.

    
ответ дан Cheers and hth. - Alf 30.10.2010 в 19:39
источник
  • Алгоритм не является полностью тривиальным, особенно когда новички на промежуточные C-кодировщики часто ошибочно используют char *, где требуется unsigned char *. Более значимые нетривиальности находятся в определении UTF-8, в частности, что вам нужно отклонить суррогатные коды и значения вне диапазона. К счастью, они не появятся в кодере, который должен обрабатывать только вход ISO-8859-1, но если вы напишете такой ограниченный кодировщик, вероятно, кто-то в конечном итоге неправильно использует его для более широкого диапазона ввода без добавления каких-либо проверок. –  R.. 31.10.2010 в 02:40
  • @ MichałLeon: Unicode не является кодировкой. Существует ряд различных кодировок Unicode, включая UTF-8 и UTF-16. Первые 256 кодовых точек Unicode совпадают с латинскими 1 (a.k.a. ISO-8859-1). Примечание: акцент не делает вас менее расходящимися с тривиальным фактом. В следующий раз, вместо крика и downvoting, рассмотрите просто проверку фактов или просто спросите обо всем, что вы не понимаете. –  Cheers and hth. - Alf 23.01.2018 в 18:23
  • @Martin: Блок кодовых точек Unicode с 128 по 255 называется «дополнением Latin-1» Unicode, потому что это то же самое, что и Latin-1. Unicode является прямым расширением Latin-1. Вы комментируете абсурдную глупость, вид техно-болтовни, который может влиять на нетехнических людей и указывает на троллинг. Я полагаю, ты троллинг. –  Cheers and hth. - Alf 24.01.2018 в 11:59
  • @ MichałLeon: Хорошо, извините. Возможно, я должен был догадаться: я много лет помогал студенту с крайне плохим зрением, и она регулярно не понимала, что там есть. Latin-1 указан в сообщении OP, в моем ответе, во всех моих комментариях и в других ответах, кроме одного. –  Cheers and hth. - Alf 24.01.2018 в 14:50
0

Вы можете использовать библиотеку boost :: locale:

Ссылка

Код будет выглядеть так:

#include <boost/locale.hpp>
std::string utf8_string = to_utf<char>(latin1_string,"Latin1");
    
ответ дан Spacemoose 31.05.2017 в 14:09
источник