【问题标题】:Problem converting char to wchar_t (length wrong)将 char 转换为 wchar_t 的问题(长度错误)
【发布时间】:2011-04-21 14:34:46
【问题描述】:

我正在尝试创建一个简单的数据结构,以方便在 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
    }
}

【问题讨论】:

  • 如果您向我们展示输出结果将会非常好。

标签: c unicode char wchar-t


【解决方案1】:

代码对我来说似乎工作正常。您能否提供更多上下文,例如您传递给它的字符串的内容,以及您使用的语言环境?

我注意到的其他一些错误/样式问题:

  • obj->length 保留为分配的长度,而不是更新以匹配(宽)字符的长度。这是你的意图吗?
  • const char * 的演员阵容既无用又糟糕。

编辑: 经讨论,您可能正在使用不符合标准的 Windows 版本的 mbstowcs 函数。如果是这样,您的问题应该更新以反映这一点。

编辑 2: 代码恰好对我有用,因为 malloc 返回了一个新鲜的零填充缓冲区。由于您将obj->length 传递给mbstowcs 作为要写入目标的wchar_t 值的最大数量,因此它将用完空间并且无法写入空终止符,除非有一个适当的多字节字符(一个源字符串中需要多个字节)。将其更改为obj->length+1,它应该可以正常工作。

【讨论】:

    【解决方案2】:

    您需要传递给mbstowcs() 的长度包括 L'\0' 终止符,但您在obj->length() 中计算的长度不包括它 - 您需要在传递的值上加 1到mbstowcs()

    此外,您应该使用mbstowcs(0, src, 0) + 1,而不是使用strlen(str) 来确定转换后字符串的长度。您还应该将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
        }
    }
    

    Mark Benningfield 指出 mbstowcs(0, src, 0) 是 C 标准的 POSIX / XSI 扩展 - 要获得仅在标准 C 下所需的长度,您必须改为使用:

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

    【讨论】:

    • @Tyler 请注意,在失败的情况下,obj->length 不会重置为 1。我不确定在这种情况下 realloc() 是否值得 - 你不妨离开分配的较大块。
    【解决方案3】:

    我在 Ubuntu linux 上以 UTF-8 作为语言环境运行它。

    以下是所要求的附加信息:

    我用一个完全分配的结构调用这个函数,并传入一个硬编码的“字符串”(不是 L“字符串”)。所以我用本质上是 setCString(*obj, "Hello!") 来调用函数。

    长度 = 6

    !C 字符串 你好!转换为 wchar 字符串 Hello!xxxxxxxxxxxxxxxxxxxx

    (其中 x = 随机数据)

    长度失败!

    供参考 printf("wcslen = %d\n",(int)wcslen(obj->string));打印为 wcslen = 11

    【讨论】:

    • 实际上我不确定 UTF-8 部分,因为我在某处读到 gcc 将所有内容默认为 UTF-32。当然,这个假设我也可能是错的......
    • 如果你为 'xxxxxxx' 输入了“随机数据”,那么mbstowcs 几乎肯定会以errno==EILSEQ 失败,返回(size_t)-1(因为“随机数据”不太可能是有效的 UTF-8),但wcslen 将报告成功转换部分的长度加上输出缓冲区中已经存在的任何垃圾,因为它不会以空值终止。
    • 不抱歉,我转换后的 wchar 字符串由于某种原因在末尾添加了随机字节,这就是问题所在。就像它没有 \0 直到它随机命中一个。我认为 mbstowcs 应该在进行转换时复制终止空字节的字符串。
    • 因为您将 obj->length 而不是 obj->length+1 传递给 mbstowcs,所以它不能以空值终止。该代码恰好对我有用,因为malloc 返回了新的(零填充)内存。如果有任何实际的多字节字符,它也将起作用,因为那时您的目的地将有额外的空间。顺便说一句,您的 memset 从来没有帮助,因为您忘记乘以 sizeof(wchar_t)(或者更好,使用 wmemset)。
    • 如果我的回答解决了您的问题,请采纳。如果没有,请跟进,以便我(或其他人)可以完成回答。
    猜你喜欢
    • 1970-01-01
    • 2012-01-27
    • 2011-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多