Как получить правильную длину std :: u8string в C ++? - PullRequest
0 голосов
/ 11 января 2020

Как получить правильную длину std :: u8string? (в C ++ 20) Я пробовал следующий код, который печатает неправильное значение длины, которое может возвращать значение числа кодовой точки.

Как я могу получить правильное значение, которое я ожидал 7, что число символов?

int main() {
    const char8_t* s = u8"Hello??";
    auto st = std::u8string(s);
    std::cout << st.size() << std::endl;
}

Ответы [ 3 ]

4 голосов
/ 11 января 2020

A u8string - это, по сути, последовательность байтов для большинства функций C ++. Таким образом, size() дает вам 13 (48 65 6c 6c 6f f0 9f 98 83 f0 9f 98 83). Символ «?» («УЛЫБАЮЩАЯ ЛИЦО С ОТКРЫТОЙ РОТОЙ» U + 1F603) кодируется как 4 элемента f0 9f 98 83. Вы увидите это с [i], substr, et c. а также.

Зная, что это UTF-8, вы можете посчитать количество кодовых точек Unicode. Вы можете использовать u32string, который является кодовыми точками. Я не верю, что в C ++ есть функции, позволяющие делать это прямо на u8string из коробки:

size_t count_codepoints(const std::u8string &str)
{
    size_t count = 0;
    for (auto &c : str)
        if ((c & 0b1100'0000) != 0b1000'0000) // Not a trailing byte
            ++count;
    return count;
}

Однако, возможно, это все еще не то, что люди считают «числом символов». Это связано с тем, что для представления одного видимого символа, «объединяющих символы», можно использовать несколько кодов. Некоторые из них также имеют «предварительно составленные» формы, и порядок объединения кодовых точек может варьироваться, что приводит к «нормальным формам» и проблемам со сравнением строк Unicode. Например, «Á» может быть «LATIN CAPITAL LETTER A WITH ACUTE» (U + 00C1) », который имеет вид UTF-8 C3 81, или может иметь нормальную« A »с« ACCUT ACCENT ACCENT (U + 0301) » "это две кодовые точки и 3 байта UTF-8 41 CC 81.

Для каждой версии Unicode есть таблицы unicode.org , которые позволяют правильно обрабатывать и преобразовывать символы объединения ( и такие вещи, как преобразование в верхний / нижний регистр), но они довольно обширные, и вам нужно написать код для их обработки. Сторонние библиотеки (я думаю, что Linux в основном использует ICU) или функции ОС (в Windows есть множество API) также предоставляют различные утилиты.

Стоит отметить, что вы можете столкнуться с этими проблемами во многих других случаях / языках. не только C ++. Например, JavaScript, Java и. NET, наряду с Windows C / C ++ API (по существу wchar_t на Windows) используют строки UTF-16, которые имеют «суррогатные пары» для некоторых кодовых точек со многими функции, фактически считающие элементы UTF-16, а не кодовые точки.

1 голос
/ 11 января 2020

Стандартный ответ c ++ состоит в том, чтобы преобразовать строку из utf8 в utf32 и затем проверить размер.

К сожалению, std::wstring_convert устарела с c ++ 17. Я понятия не имею, какой будет замена.

#include <string>
#include <iostream>
#include <cstdlib>
#include <locale>
#include <codecvt>

auto convert(std::u8string input) -> std::u32string
{
    auto first = reinterpret_cast<const char*>(input.data());
    auto last = first + input.size();

    auto result = std::u32string();

    std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> ucs4conv;
    try
    {
        result = ucs4conv.from_bytes(first, last);
    }
    catch(const std::range_error& e) {
        last = first + ucs4conv.converted();
        std::clog << "UCS4 failed after consuming " << std::dec << std::distance(first, last) <<" characters:\n";
        result = ucs4conv.from_bytes(first, last);
    }

    return result;
}

int main() {
    const char8_t* s = u8"Hello??";
    auto st = std::u8string(s);
    std::cout << "bytes      : " << st.size() << std::endl;

    auto ws = convert(st);
    std::cout << "wide chars : " << ws.size() << std::endl;
}

ожидаемый результат:

bytes      : 13
wide chars : 7

https://godbolt.org/z/Z0a6bb

0 голосов
/ 30 января 2020

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

И на самом деле, я не собираюсь делать точку сам. Вместо этого я просто предоставлю ссылку на отличный пост в блоге, который объясняет проблемы, чтобы вы могли оценить, какая информация вам действительно нужна.

https://hsivonen.fi/string-length

...