Проблема преобразования char в wchar_t (неправильная длина) - PullRequest
2 голосов
/ 06 октября 2010

Я пытаюсь создать простую структуру данных, которая упростит преобразование между строками ASCII и Unicode. Моя проблема в том, что длина, возвращаемая функцией mbstowcs, является правильной, а длина, возвращаемая функцией wcslen, во вновь созданной строке wchar_t - нет. Я что-то здесь упускаю?

typedef struct{

    wchar_t *string;
    long length; // I have also tried int, and size_t
} String;

void setCString(String *obj, char *str){

    obj->length = strlen(str);

    free(obj->string); // Free original string
    obj->string = (wchar_t *)malloc((obj->length + 1) * sizeof(wchar_t)); //Allocate space for new string to be copied to

    //memset(obj->string,'\0',(obj->length + 1)); NOTE: I tried this but it doesn't make any difference

    size_t length = 0;

    length = mbstowcs(obj->string, (const char *)str, obj->length);

    printf("Length = %d\n",(int)length); // Prints correct length
    printf("!C string %s converted to wchar string %ls\n",str,obj->string); //obj->string is of a wcslen size larger than Length above...

    if(length != wcslen(obj->string))
            printf("Length failure!\n");

    if(length == -1)
    {
        //Conversion failed, set string to NULL terminated character
        free(obj->string);
        obj->string = (wchar_t *)malloc(sizeof(wchar_t));
        obj->string = L'\0';
    }
    else
    {
        //Conversion worked! but wcslen (and printf("%ls)) show the string is actually larger than length
        //do stuff
    }
}

Ответы [ 3 ]

2 голосов
/ 06 октября 2010

Код, кажется, работает нормально для меня.Можете ли вы предоставить больше контекста, такого как содержимое строк, которые вы передаете ему, и какую локаль вы используете?

Несколько других ошибок / стилей, которые я заметил:

  • obj->length оставляется как выделенная длина, а не обновляется, чтобы соответствовать длине в (широких) символах.Это ваше намерение?
  • Приведение к const char * бесполезно и дурно.

Редактировать: При обсуждении похоже, что вывозможно, используется несовместимая версия Windows mbstowcs.Если это так, ваш вопрос должен быть обновлен, чтобы отразить его таким образом.

Редактировать 2: Код работал только для меня, потому что malloc возвратил новый, заполненный нулямибуфер.Так как вы передаете от obj->length до mbstowcs как максимальное количество wchar_t значений для записи в место назначения, ему не хватит места и вы не сможете записать нулевой терминатор, если нет подходящего многобайтового символа (которыйтребуется более одного байта) в исходной строке.Измените это на obj->length+1, и оно должно работать нормально.

1 голос
/ 06 октября 2010

Длина, которую нужно передать mbstowcs() , включает символ L'\0' терминатора, но ваша расчетная длина в obj->length() не включает его - вам нужно добавить 1 к значению, переданному mbstowcs().

Кроме того, вместо использования strlen(str) для определения длины преобразованной строки следует использовать mbstowcs(0, src, 0) + 1. Вы также должны изменить тип str на const char * и исключить состав. realloc() может использоваться вместо пары free() / malloc(). В целом, это должно выглядеть так:

typedef struct {
    wchar_t *string;
    size_t length;
} String;

void setCString(String *obj, const char *str)
{
    obj->length = mbstowcs(0, src, 0);
    obj->string = realloc(obj->string, (obj->length + 1) * sizeof(wchar_t)); 

    size_t length = mbstowcs(obj->string, str, obj->length + 1);

    printf("Length = %zu\n", length);
    printf("!C string %s converted to wchar string %ls\n", str, obj->string);

    if (length != wcslen(obj->string))
            printf("Length failure!\n");

    if (length == (size_t)-1)
    {
        //Conversion failed, set string to NULL terminated character
        obj->string = realloc(obj->string, sizeof(wchar_t));
        obj->string = L'\0';
    }
    else
    {
        //Conversion worked!
        //do stuff
    }
}

Марк Беннингфилд отмечает, что mbstowcs(0, src, 0) является расширением POSIX / XSI стандарта C - чтобы получить необходимую длину только по стандарту C, вместо этого вы должны использовать:

    const char *src_copy = src;
    obj->length = mbstowcs(NULL, &src_copy, 0, NULL);
0 голосов
/ 06 октября 2010

Я запускаю это на Ubuntu linux с UTF-8 в качестве локали.

Вот дополнительная информация по запросу:

Я вызываю эту функцию с полностью распределенной структурой и передаюжестко закодированная «строка» (не L-строка «).поэтому я вызываю функцию с тем, что по сути является setCString (* obj, "Hello!").

Length = 6

! C string Hello!преобразовано в строку wchar. Hello! xxxxxxxxxxxxxxxxxxxx

(где x = случайные данные)

Ошибка длины!

для ссылки printf ("wcslen =% d \ n", (int) wcslen (obj-> строка));распечатывается как wcslen = 11

...