2010-10-06 2 views
2

Я пытаюсь создать простую структуру данных, которая упростит преобразование между строками ASCII и строками Unicode. Моя проблема в том, что длина, возвращаемая функцией mbstowcs, верна, но длина, возвращаемая функцией wcslen, на вновь созданной строке wchar_t, отсутствует. Я что-то упустил?Проблема преобразования char в 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 
    } 
} 
+1

Так много хорошего пришло бы, если бы вы показали нам выход. –

ответ

1

Длина вам необходимо пройти 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 
    } 
} 

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

const char *src_copy = src; 
    obj->length = mbstowcs(NULL, &src_copy, 0, NULL); 
+0

Работает идеально. Спасибо caf и R ... – Tyler

+0

@Tyler обратите внимание, что в случае сбоя 'obj-> length' не сбрасывается на 1. Я не уверен, что' realloc() 'в этом случае того стоит, либо - вы также можете оставить выделенный выделенный блок. – caf

2

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

Несколько вопросов других ошибок/стиля Я заметил:

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

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

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

0

Я запускаю это на Ubuntu linux с UTF-8 как локаль.

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

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

Длина = 6

! C string Hello! преобразуется в WChar строку Здравствуйте! xxxxxxxxxxxxxxxxxxxx

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

недостаточность Длина!

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

+0

На самом деле я не уверен в части UTF-8, потому что я где-то читал, что gcc по умолчанию ставит все под UTF-32. Конечно, я мог ошибаться и в этом предположении ... – Tyler

+0

Если вы поставили «случайные данные» на «xxxxxxx», то «mbstowcs» почти наверняка завершится ошибкой с 'errno == EILSEQ', возвращая' (size_t) -1' (поскольку «случайные данные» вряд ли будут действительны UTF-8), но 'wcslen' будет сообщать длину успешно конвертированной части плюс любой нежелательный файл уже в выходном буфере, поскольку он не получит нуль. –

+0

Нет, извините, моя переделанная строка wchar по каким-то причинам имеет случайные байты на конце, вот в чем проблема. Это похоже на то, что у него нет \ 0, пока оно случайно не попадет на него. Я думал, что mbstowcs должен был скопировать строку с завершающим нулевым байтом (s?), Когда он сделал преобразование. – Tyler

Смежные вопросы