如何将单字节的const char*转换为UTF-8编码

9

我有一个需要传递由char*指向的UTF-8字符串的函数,但我只有指向单字节字符串的char指针。在C++中,如何将该字符串转换为UTF-8编码?是否有可用的代码可以实现这一点? 谢谢!


7
你的原始字符串采用了哪种编码格式? - Yakov Galka
Linux上的路径名不强制使用特定的编码,唯一的规则是它不能包含/。因此,任何人都可以使用任何编码创建文件名,甚至可以使用在任何给定字符集中非法编码的文件名。您可以猜测它是ISO8859-1,并使用iconv()函数进行转换。 - nos
3个回答

4
假设您使用Linux系统,您需要使用iconv来进行转换。当您打开转换器(iconv_open)时,需要传递fromto编码。如果您将空字符串作为from传递,则会从系统上使用的区域设置进行转换,这应该与文件系统匹配。
在Windows上,您可以使用MultiByteToWideChar完成基本相同的操作,其中将CP_ACP作为代码页进行传递。但是,在Windows上,您可以直接调用Unicode版本的函数以立即获取Unicode,然后使用WideCharToMultiByteCP_UTF8进行转换为UTF-8。

2
将字符串转换为不同的字符编码,可以使用各种字符编码库。其中一种流行的选择是 iconv(在大多数 Linux 系统上是标准)。
但是,要做到这一点,您首先需要确定输入的编码方式。不幸的是,这并没有通用的解决方案。如果输入未指定其编码方式(例如,Web 页面通常会这样做),您必须猜测。
至于您的问题:您写道,您从 FAT32 文件系统调用 readdir 获得字符串。我不太确定,但我认为 readdir 将按文件系统存储的方式返回文件名。对于 FAT/FAT32:
- 短文件名采用某些 DOS 代码页 编码 - 该代码页取决于文件的编写方式,只从文件系统无法判断。 - 长文件名采用 UTF-16 编码。
如果您使用标准的vfat Linux内核模块来访问FAT32分区,您应该可以从readdir中获取长文件名(除非文件只有8.3个字符的名称)。 FAT32在内部以UTF-16格式存储长文件名。 vfat驱动程序将把它们转换为由iocharset=挂载参数给定的编码(默认为系统默认编码,我认为)。
其他信息:
您可能需要调整挂载选项codepageiocharset(请参见http://linux.die.net/man/8/mount),以便在FAT32卷上正确显示文件名。尝试进行挂载,使得Linux控制台中的文件名显示正确,然后继续操作。这里还有一些更多的解释:http://www.nslu2-linux.org/wiki/HowTo/MountFATFileSystems

最后一个链接解决了我的特定问题。看起来,只需使用适当的参数挂载设备即可使其正常工作。我将其标记为已接受的答案,因为它解决了我的具体情况,并正确解释了一般情况。无论如何,感谢大家! - Luca Carlon

1

我猜测1字节字符串的最高位被设置了,因此你要传递给它的函数需要传递超过1字节。

首先,以十六进制打印字符串。

例如:

unsigned char* str = "your string";
for (int i = 0; i < strlen(str); i++)
  printf("[%02x]", str[i]);

现在请仔细阅读Wikipedia上关于UTF8编码的文章,其中详细解释了它的工作原理。
http://en.wikipedia.org/wiki/UTF-8

UTF-8是一种可变长度编码方式,每个字符可以占据1到4个字节。

因此,将十六进制转换成二进制并查看代码点。例如,如果第一个字节以二进制的11110开头,则期望的字符串由4个字节组成。由于ASCII是7位0-127,最高位始终为零,因此应该只有一个字节。顺便说一句,在UTF8字符串的宽字符中,紧随第一个字节后面的字节将以“10...”开头,这些是续补码……这就是您的函数报错的原因所在,即期望的续补码缺失了。因此,该字符串并不完全符合ASCII规范,正如您原先认为的那样。

您可以使用像iconv这样的工具进行转换,或者使用这个库文件:http://utfcpp.sourceforge.net/


我尝试使用字符串“/system/mnt/usb0/audio/07 Dracula Der Pfähler.mp3”,结果得到了[2f][73][79][73][74][65][6d][2f][6d][6e][74][2f][75][73][62][30][2f][61][75][64][69][6f][2f][30][37][20][44][72][61][63][75][6c][61][20][44][65][72][20][50][66][e4][68][6c][65][72][2e][6d][70][33]。这似乎很奇怪,因为字符ä是ASCII集的一部分,所以应该没问题。我错了吗?谢谢! - Luca Carlon
@Luca - 是的,你错了(抱歉)。ASCII英文字符(小于0x80)与它们的UTF-8等效字符是相同的。ä 转换为 e4,这不是合法的UTF-8编码。 - Michael J
啊,我明白了!好的,所以UTF-8与扩展ASCII不兼容。这就解释了为什么我注意到'è'和'ä'都有问题。谢谢! - Luca Carlon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接