LINUX.ORG.RU

c++ и utf-8

 , , , ,


6

6

Допустим есть файл сохранённый в UTF8. Читать я его могу исключительно std::ifstream (без std::wifstream).

После прочтения файла, я хочу иметь возможность итерироваться по utf-8 символам, и даже сравнивать их

for (size_t i = 0; i < utf8String.size(); i++) {
    if (utf8String[i] == 'ф') {
        //...
    }
}

Т.е. я хочу чтобы была возможность работать с каждым utf-8 символом, в независимости от того какой он длинны как с одним символом.

Что мне для этого нужно?

Я думал что wchar_t. Но есть такие два источника: 1. https://ru.wikipedia.org/wiki/Широкий_символ 2. https://stackoverflow.com/questions/17871880/should-i-use-wchar-t-when-using-...

Которые вроде как говорят что это не очень хорошо.

Еще я заметил (да на ++ уже пишу и не первый год), но заметил только сейчас, т.к.раньше просто не думал про это ибо не было нужно:

что std::wstring wstr = L"добро"; //да, система дебиан 8, x86-64, исходник сохранен в utf8.

в общем wstr==«добро» в оперативной памяти будет представлено не в utf-8 а в неведомой кодировке. Но по которой можно итерироваться и сравнивать.

Буква 'д' из этой строки будет иметь следующий байт-код 00110100 00000100 00000000 00000000 (wchar_t)

При этом если бы 'д' была в utf8 то она должна была бы иметь такие байты 11010000 10110100 {00000000 00000000} - в скобках хвост который как бы не имеет отношение к коду 'д', но заполняет тип wchar_t.

Вопросы: Можно ли как-то сделать так чтобы компилятор (g++) видя wchar_t c = L'д' или whchar_t c = 'д' - конструировал utf8 букву в wchar_t типе, а не в непонятной кодировке. И кстати в какой кодировке он её кодирует по умолчанию?

Есть ли какой-то способ прочитать преобразовать utf-8 строку, хранимую в std::string в std::wstring так чтобы после такого преобразования содержимое этой wstring можно было бы корректно вывести в std::wcout?

Я нашел такой способ Преобразование std::string в std::wstring

Но во первых он какой-то страншный и не очень понятный, какие-то шаблоны актуализируются...

А самое главное в моём debian8 и gcc4.3 нет такого хедера include <codecvt>

Поэтому у кого поновее пакетная база, и кому не лень, пожалуйста из ссылки выше запустите пример. Сделайте std::string str(«добро») и преобразуйте её в std::wstring Нужно чтобы после преобразования wstring строка корректно выводилась в std::wcout и можно было побуквенно (а не побайтово) итерироваться по ней, и посимвольно сравнивать.

В общем, т.к. у меня нет этого codecvt я сделал преобразование руками (да можно красивее, переносимее (учитывать порядок байт), при вызове из main не выходить за границы строки и т.д. и т.п. - не суть): https://pastebin.com/4E3nuNcM

и вот если приблизительно таким методом конструировать std::wstring которая содержит utf-8 можно будет итерироваться по ней (да, я знаю что utf8 может быть длиннее чем размер wchar_t, но у меня будет набор латиницы и кириллицы из utf8 документа), можно будет сравнивать с символами, но к сожалению не так wstr[0] = 'ы' а только с заранее созданными символами, подобным методом как строка создавалась. wstr[0] == wcharSymbol.

Это не удобно, а еще такая строка не может корректно выводится на std::wcout.

Т.е. заключительный вопрос - можно ли как-то пользуя нативный wchar_t работать посимвольно с utf-8 в c++ в линукс, имея полный ф-л такой как итерации, посимвольное сравнение, корректный вывод в std::wcout.

Если нельзя - есть ли возможно какие-то сторонние (причем легковесные библиотеки, код которых можно включить в проект, и собрать статически, и чтобы места не много тратили) которые предоставляют некий тип wideChar, полностью совместимый и с std::wcout и со всей stl(конейнерами, алгоритмами) и при этом желательно кросплатформенная?

Кстати в презренной винде на этом же наборе символов utf8 (латиница и кириллица) - всё очень хорошо (плохо там будет когда потребуется символ длиннее 2 байт) - т.к. там wchar_t это 2 байта, т.е. он как раз отлично соотвествует, и в wcout тоже выводится :)

Просьба не флудить а по конкретике писать :)

★★★★★

Последнее исправление: bonta (всего исправлений: 3)

боль, это боль, как ее ты не назови

robotron5
()

ТС, с UTF-8 все не так просто.


П̛͇̘̤̪ͪ͞р̺̜̘͕̬̥̩̜͖ͬ͢о̧͓͍̘̝̘͙͉̆͌͊ͩ̀ͨ͠ͅч͙͎̥̬̫̱͔̒͐͛̔͆͐̓̚у̝̪̬͖̅̓ͣͤ͆̄̈̿̀͘в̸̜̹̪͚̝̆ͅс̊̐ͮ̇҉̛̻̱̙͕͈̗т̶̷̧̪̼͙̭̝̃̇̂̽̑̅в̨͉̮̳̥̯͔̮̽̐̆̉ͪ̿ͫ̋͠у̖̠̞̰͙̙͕͗̎̃͒͢͝й̛̛̪̬ͤ̎͗͂̐ͫ ͙͉̬̱͉̜̒̊̇ͯ̉ͧ͘г͖̬ͩ͘̕л͙̣͍̥̪͙̪̹̝̌̋ў̵̧̺̤̲̠̠͚̮ͭ̊б̷̸̙̹̮̼̠͉̰ͭ̏̇ӥ̵̤̥̱̘ͪ͂̾̽̚н̫̫͈̮ͨ̂͊̐̚у̸̗̹̮̮͖͙͈͆͗̾́͢ ̴͚͉̐͒ͩͯͪͨ̉ͦс̢̟̤̓͡в͉̳ͩ̇̔ͧо̠͍͋̉̊̾̎̂͜е̷̱͈̼͕̝͈͇ͪ̍͛͗ͨ̂͘й̸̨͇̟̜̘̝̠͈͕ͯ̈̔͂̂̐̉̾ ̴̱̼̯̬̬̹̘ͯ̀͂ͤͣ̓б̱͓̫͔ͮ̐о̙̜̜͙͈͇̱̹͕ͭ̅̏̔̚͠л͋͌͢͏̳̙͉̲̮̙͇и̼̲̬̙̙̟̮̐̅ͥͮ̃ͦ͗͋ͬ.͇͉̠̜̳̜̑ͭ̏̈̈́ͣ

Как с таким работать будешь? Тут, конечно, перебор для пущего эффекта, но всё же в том же немецком есть буквы с точками, везде есть ударения и так далее.

Подсказка: в UTF-8 есть не только буквы...

peregrine ★★★★★
()
Последнее исправление: peregrine (всего исправлений: 1)
Ответ на: комментарий от peregrine

в UTF-8 есть не только буквы...

А ещё и модификаторы, да.

Поэтому и рулят однобайтные кодировки включая KOI8-R.

saahriktu ★★★★★
()
Ответ на: комментарий от RazrFalcon

Во, верный подход. Есть ещё вариант перекодировать UTF-8 в UTF-32 и работать уже с ним, но умлауты это не отменяет.

peregrine ★★★★★
()

QString из Qt, GLib::ustring из glibmm. Забудь уже эти STL-ные строки, как страшный сон.

meliafaro ★★★★★
()

После прочтения файла, я хочу иметь возможность итерироваться по utf-8 символам, и даже сравнивать их

for (size_t i = 0; i < utf8String.size(); i++) {
    if (utf8String[i] == 'ф') {
        //...
    }
}

Вы считаете что это С++ и итератор? Отсюда вся боль.

MKuznetsov ★★★★★
()
Ответ на: комментарий от RazrFalcon

По логике нужно вообще icu использовать, но ТС она не подходит, ибо весит почти 50 метров.

Не увидел у него требований к размеру библиотек.

peregrine ★★★★★
()
Ответ на: комментарий от peregrine

причем легковесные библиотеки, код которых можно включить в проект, и собрать статически, и чтобы места не много тратили

RazrFalcon ★★★★★
()
Ответ на: комментарий от RazrFalcon

Легковесность относительное понятие. Сейчас на винтах 1 тб и выше, а оперативка перевалила за 10 гб.

peregrine ★★★★★
()
Последнее исправление: peregrine (всего исправлений: 1)
Ответ на: комментарий от peregrine

всё же в том же немецком есть буквы с точками, везде есть ударения и так далее.

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

Thetan ★★★
()
Ответ на: комментарий от RazrFalcon

Не подходит для эмодзи. Они могут быть и по 6 байт.

Весь юникод с запасом влазит в 32-битное целое. Это при кодировании в UTF-8 длина последовательности может теоретически достигать 6 байт.

i-rinat ★★★★★
()
Ответ на: комментарий от MKuznetsov

Вы считаете что это С++ и итератор? Отсюда вся боль.

О да, а вот если обмазаться стл-ными итераторами по контейнерам функторных аллокаторов, то вся боль сразу же пройдёт.

anonymous
()
Ответ на: комментарий от MKuznetsov

и да, и нет. я разделяю понятие итератор на соотвествуюющие классы, о чем видимо Вы и решили заметить :)

Но а вообще по итерированием я имею ввиду именно высокий уровень а не уровень деталей. Так что - да, это с++ итератор. Ибо ++ дает смещение индеса.

Так же как в литературе по я.п. есть обще-устоявшееся название «ссылка» но переводя в мир Си++ это как правило не type& и не type&& и даже не type* - а просто высокоуровневое понятие не прямой передачи объекта

bonta ★★★★★
() автор топика
Последнее исправление: bonta (всего исправлений: 1)

В этом и задача wchar_t - вместить любой символ

wchar_t - type for wide character representation (see wide strings). Required to be large enough to represent any supported character code point (32 bits on systems that support Unicode. A notable exception is Windows, where wchar_t is 16 bits and holds UTF-16 code units) It has the same size, signedness, and alignment as one of the integer types, but is a distinct type.

pavlick ★★
()
Последнее исправление: pavlick (всего исправлений: 1)
Ответ на: комментарий от i-rinat

Если преобразовать в UTF-32

Инкрементирую как предложение.

ТС, конвертируй utf-8 в Unicode (не кодированный / non encoded). Unicode == UTF-32 == UCS-4. Тогда каждый code point занимает ровно 4 байта.

KennyMinigun ★★★★★
()
Ответ на: комментарий от RazrFalcon

Это как?

Количество октетов  Значащих бит  Шаблон
1                   7             0xxxxxxx
2                   11            110xxxxx 10xxxxxx
3                   16            1110xxxx 10xxxxxx 10xxxxxx
4                   21            11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
5                   26            111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
6                   31            1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
i-rinat ★★★★★
()
Ответ на: комментарий от bonta

просто таким «высокоуровневым» решением вы потребовали от класса utf8String быстрого оператора [], то есть либо индексации строки utf либо преобразования её в массив wide. И упёрлись в размерность wchar_t и портабельность

При том что «итерироваться» (последовательно перебирать символы) и сравнивать можно прямо в utf, и даже не порождая класс. Вам же только это надо ? редактирование и перекодировка не входят в задачи

Кстати тут, на ЛОР, кто-то уже делал довольно толковую и быструю библиотеку с такими-же целями - просто поспрашайте.

MKuznetsov ★★★★★
()
Ответ на: комментарий от pavlick

У вас даже в цитате написано, что это не сработает.

RazrFalcon ★★★★★
()

Допустим есть файл сохранённый в UTF8. Читать я его могу исключительно std::ifstream (без std::wifstream).

Почему не можете? Нельзя или не получится? У меня на общесистемный utf8, файл «file» в utf8

#include <fstream>
#include <string>
#include <iostream>
using namespace std;

int main()
{
   wstring s;
   wifstream f{"file"};
   f. sync_with_stdio(false);
   wcout.imbue(locale{""});
   f.imbue(locale{""});
   f >> s;

   for(wchar_t c : s)
      wcout << c;
   wcout << endl;
   
   return 0;
}
wcout: Привет

pavlick ★★
()
Ответ на: комментарий от RazrFalcon

4 characters в 14 байт всего. Формально это 4 символа, правда как и с умлаутами считать за 1 букву. Отдельного character более чем в 4 байта нет, так что тот же UTF-32 годен до тех пор, пока не становится вопрос в обработке фактических символов.

peregrine ★★★★★
()
Ответ на: комментарий от RazrFalcon

x — это места под биты данных. В UTF-8 раньше определялось кодирование вплоть до шести байт, хотя теоретически можно сделать до восьми. Но так как сейчас больше четырёх не надо, то даже в rfc3629 упоминается только четыре.

Забавно, что кодирование в UTF-8 не однозначное, потому что двухбайтная последовательность покрывает все точки однобайтной, трёхбайтная покрывает всё, что могут кодировать двухбайтные и однобайтные, и так далее.

i-rinat ★★★★★
()

Я вот эту С-шную библиотеку использую. В принципе, для плюсов можно обертку сделать. Декодирую всё в Unicode-буфер (uint32_t*), делаю всё, что надо в своём коде, обратно отдаю кодированную в UTF-8 строку.
Не уверен, что это самый правильный метод, но менее болезненного как-то не придумалось.

SkyMaverick ★★★★★
()
Последнее исправление: SkyMaverick (всего исправлений: 1)
Ответ на: комментарий от i-rinat

Понял. Я в такие дебри не опускался. Просто была задача, когда нужно было дробить строку на символы. И вот там я встретил пример с флагом, ака Compound emojis.

RazrFalcon ★★★★★
()
Ответ на: комментарий от RazrFalcon

Похоже вы не поняли, что там такое. один символ UTF-32 может кодировать 2^31 символов, что полностью совпадает с теоретическим максимумом UTF-8. Однако, для совместимости с UTF-16 UTF-8, как и весь юникод понерфили до 4 бит на символ максимум, оставив, правда случаи когда один фактический символ кодируется несколькими реальными. Короче, читать до просветления. https://ru.wikipedia.org/wiki/Юникод

peregrine ★★★★★
()
Ответ на: комментарий от peregrine

А вот что из веселого есть, с чем я реально сталкивался — в юникоде есть символы, которые считаются за 1, но даже в некоторых моноширинных шрифтах вместо одного знакоместа занимают больше или меньше, что не может не радовать. Я проблевался когда когда-то хотел текст для коньков в 2 колонки отдавать и наткнулся на такое. Сравните:

Here is an example
Here is an example
Радоваться по запросу в гугле
Полуширинные и полноширинные формы

peregrine ★★★★★
()
Последнее исправление: peregrine (всего исправлений: 1)
Ответ на: комментарий от MKuznetsov

(лень логиниться с другого браузера), да нет, мне не только итерироваться нужно, но и сравнивать и редактировать, т.е. полный ф-л желателен.

В портабельность конечно же упёрся, спору нет :)

В общем я нашел каки-ето либы, а еще увидел что в pugi-xml при открытии документа указывается параметр с таким описанием:

encoding_utf8 corresponds to UTF-8 encoding as defined in the Unicode standard; UTF-8 sequences with length equal to 5 or 6 are not standard and are rejected.

т.е. прям вообще очень похоже на то что они c чем-то типа wchar_t работают.

И вот в проекте, все то что читается из xml вроде бы как потом (но это не точно) может и нормально выходить в std::wcout и итерироваться посимвольно и сравниваться посимвольно.

Вот возможно посмотрю в эту тему - в какую кодировку кодирует pugi. и сделаю ф-ю которая делает так же (если это просто), если нет - то сторонние либы смотреть.

anonymous
()
Ответ на: комментарий от anonymous

encoding_utf8 corresponds to UTF-8 encoding as defined in the Unicode standard; UTF-8 sequences with length equal to 5 or 6 are not standard and are rejected.
т.е. прям вообще очень похоже на то что они c чем-то типа wchar_t работают.

Почитайте мои посты, я писал почему так. Дело не в wchar_t, который имеет право быть хоть 8 бит, если автор компилятора так решил. Вообще wchar_t лучше не использовать, если софт пишется не под один компилятор и одну платформу. Есть char32_t который должен быть с C++11 и гарантирует свои размеры.

peregrine ★★★★★
()
Последнее исправление: peregrine (всего исправлений: 2)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.