Я пытаюсь преобразовать строку Unicode в строку UTF8:

#include <stdio.h>
#include <string>
#include <atlconv.h>
#include <atlstr.h>

using namespace std;

CStringA ConvertUnicodeToUTF8(const CStringW& uni)
{
    if (uni.IsEmpty()) return "";
    CStringA utf8;
    int cc = 0;

    if ((cc = WideCharToMultiByte(CP_UTF8, 0, uni, -1, NULL, 0, 0, 0) - 1) > 0)
    {
        char *buf = utf8.GetBuffer(cc);
        if (buf) WideCharToMultiByte(CP_UTF8, 0, uni, -1, buf, cc, 0, 0);
        utf8.ReleaseBuffer();
    }
    return utf8;
}

int main(void)
{
    string u8str = ConvertUnicodeToUTF8(L"gökhan");

    printf("%d\n", u8str.size());

    return 0;
}

Мой вопрос: должно ли возвращаемое значение u8str.size () быть 6? Он печатает 7 сейчас!

1
codeator 26 Ноя 2016 в 09:35

3 ответа

Лучший ответ

7 правильно. Символ не ASCII ö кодируется двумя байтами.

2
David Heffernan 26 Ноя 2016 в 06:59

По определению «многобайтный» означает, что каждый объект Unicode может занимать до 6 байтов, см. Здесь: Сколько байтов занимает один символ Юникода?

Дополнительная литература: http://www.joelonsoftware.com/articles/Unicode.html

2
Community 23 Май 2017 в 10:30

Кодовая точка Unicode использует 2 или 4 байта в UTF-16, но использует 1-4 байта в UTF-8, в зависимости от его значения. Для 2-байтового значения кодовой точки в UTF-16 возможно использование 3-4 байтов в UTF-8, таким образом, строка UTF-8 может использовать больше байтов, чем соответствующая строка UTF-16. UTF-8 имеет тенденцию быть более компактным для латинских / западных языков, но UTF-16 имеет тенденцию быть более компактным для языков Восточной Азии.

std::(w)string::size() и CStringT::GetLength() подсчитывают количество закодированных кодовых единиц, а не количество кодовых точек. В вашем примере "gökhan" кодируется как:

UTF-16LE: 0x0067 0x00f6 0x006b 0x0068 0x0061 0x006e
UTF-16BE: 0x6700 0xf600 0x6b00 0x6800 0x6100 0x6e00
UTF-8: 0x67 0xc3 0xb6 0x6b 0x68 0x61 0x6e

Обратите внимание, что ö кодируется с использованием 1 кодовой единицы в UTF-16 (LE: 0x00f6, BE: 0xf600), но использует 2 кодовых модуля в UTF-8 (0xc3 0xb6). Вот почему ваша строка UTF-8 имеет размер 7 вместо 6.

При этом при вызове WideCharToMultiByte() и MultiByteToWideChar() с -1 в качестве исходной длины функция должна вручную подсчитывать символы, и возвращаемое значение будет включать место для нулевого терминатора, когда указатель назначения ЗНАЧЕНИЕ NULL. Вам не нужно это дополнительное пространство при использовании CStringA/W, std::(w)string и т. Д., И вам не нужны накладные расходы на подсчет символов, когда источнику уже известна его длина. Вы всегда должны указывать фактическую длину источника, если вы ее знаете, например:

CStringA ConvertUnicodeToUTF8(const CStringW& uni)
{
    CStringA utf8;

    int cc = WideCharToMultiByte(CP_UTF8, 0, uni, uni.GetLength(), NULL, 0, 0, 0);
    if (cc > 0)
    {
        char *buf = utf8.GetBuffer(cc);
        if (buf)
        {
            cc = WideCharToMultiByte(CP_UTF8, 0, uni, uni.GetLength(), buf, cc, 0, 0);
            utf8.ReleaseBuffer(cc);
        }
    }

    return utf8;
}
0
Remy Lebeau 26 Ноя 2016 в 23:31