将Unicode字符串转换为utf-8或utf-16字符串的方法是什么?

6

如何将Unicode字符串转换为utf-8或utf-16字符串? 我的VS2005项目使用Unicode字符集,而cpp中的sqlite提供了

int sqlite3_open(
  const char *filename,   /* Database filename (UTF-8) */
  sqlite3 **ppDb          /* OUT: SQLite db handle */
);
int sqlite3_open16(
  const void *filename,   /* Database filename (UTF-16) */
  sqlite3 **ppDb          /* OUT: SQLite db handle */
);

如何打开文件夹。

我该如何将字符串、CString或wstring转换为UTF-8或UTF-16字符集?

非常感谢!

5个回答

7
使用 WideCharToMultiByte 函数进行翻译。将 CodePage 参数指定为 CP_UTF8
CHAR buf[256]; // or whatever
WideCharToMultiByte(
  CP_UTF8, 
  0, 
  StringToConvert, // the string you have
  -1, // length of the string - set -1 to indicate it is null terminated
  buf, // output
  __countof(buf), // size of the buffer in bytes - if you leave it zero the return value is the length required for the output buffer
  NULL,    
  NULL
);

此外,在Windows中,Unicode应用程序的默认编码是UTF-16LE,因此您可能不需要执行任何翻译,只需使用第二个版本sqlite3_open16


我不建议使用固定缓冲区;相反,使用动态分配的缓冲区(例如std::vector),根据需要进行扩展(当WideCharToMultiByte告诉您字符串太小时)。 - C. K. Young
1
我不得不反对:您展示了如何从UTF16转换为UTF8。这不是OP的要求,因为似乎有一个可用于宽字符字符串的函数:sqlite3_open16()。在我看来,正确的答案是:使用sqlite3_open16()。 - Serge Wautier
@Chris,这就是为什么我说“或者别的什么”并在输出缓冲区大小上加注释的原因 - 我不想把事情搞得太复杂。 - 1800 INFORMATION

7

简短回答:

如果你使用Unicode字符串,如CString或wstring,则无需进行转换。使用sqlite3_open16()。 您必须确保将WCHAR指针(转换为 void * )传递给API。例如对于CString:(void *)(LPCWSTR)strFilename

较长的答案:

您没有要转换为UTF8或UTF16的Unicode字符串。您在程序中使用给定编码表示的Unicode字符串:Unicode本身并不是一种二进制表示形式。编码表示Unicode代码点(数值)在内存中的表示方式(数字的二进制布局)。UTF8和UTF16是最广泛使用的编码。它们非常不同。

当VS项目说“Unicode字符集”时,实际上意味着“字符被编码为UTF16”。因此,您可以直接使用sqlite3_open16()。无需进行转换。字符存储在WCHAR类型中(而不是 char ),该类型占用16位(在Win32上采用标准C类型 wchar_t ,占用16位。其他平台可能不同。感谢Checkers的更正)。

还有一个细节需要注意:UTF16有两种不同的变体:Big Endian和Little Endian。这是这16位的字节顺序。您提供的UTF16函数原型没有说明使用哪个排序。但是您可以相当安全地假设sqlite使用与Windows相同的端序(我IRC的Little Endian。我知道顺序但一直对名称有问题:-))。

编辑:Checkers的评论的答案:

UTF16使用16位代码单元。在Win32下(仅在Win32下), wchar_t 用于这种存储单元。诀窍在于,某些Unicode字符需要一个由2个这样的16位代码单元组成的序列。它们被称为代理对。

与UTF8表示使用1到4个字节序列表示1个字符的方式相同。但是UTF8与 char 类型一起使用。


3
不,不,不!sqlite3_open16()使用'void *'参数,因为它被声明为UTF16,而不是wchar_t,在不同平台上尺寸不同且可能不是UTF16(例如glibc的wchar_t为4字节)。 - Alex B
1
是的,我知道UTF16表示法。但是,你不能假设wchar_t的内部表示在所有平台上都是相同的,事实并非如此。 - Alex B
1
实际上,我会说UTF16使用16位代码(而不是字符),就像UTF8使用8位(八位)代码一样。一个Unicode字符代码(最多20位)将需要1个UTF16代码来表示常用字符,但对于其他字符则需要两个代码(称为代理对)。 - orcmid
转换可能仍然需要:wchar_t不能保证为16位,在非Windows系统上更有可能为32位。这对于发帖者可能不相关,但对于其他人来说可能是非常重要的细节。 - Wichert Akkerman
术语是“代码单元”(code unit)。(http://www.unicode.org/glossary/#code_unit)。单位意味着它是最小的构建块:可能需要一个或多个。 - Tom Blodget
显示剩余5条评论

3
所有的C++字符串类型都是字符集中性的。它们只确定了一个字符宽度,不做进一步的假设。在Windows中,wstring使用16位字符,大致对应于utf-16,但它仍然取决于您在线程中存储的内容。wstring并不以任何方式强制要求您放入其中的数据必须是有效的utf16。当UNICODE被定义时,Windows使用utf16,所以您的字符串很可能已经是utf16了,您不需要做任何事情。
一些人建议使用WideCharToMultiByte函数来将utf16转换为utf8,这是(其中之一)转换方法。但由于sqlite可以处理utf16,因此这不是必需的。

0

utf-8和utf-16都是“unicode”字符编码。你可能要谈论的是utf-32,它是一种固定大小的字符编码。也许搜索

"将utf-32转换为utf-8或utf-16"

会为您提供一些结果或其他论文。


0

最简单的方法是使用CStringA。CString类是一个typedef,可以是CStringA(ASCII版本)或CStringW(宽字符版本)。这两个类都有构造函数来转换字符串类型。我通常使用:

sqlite3_open(CStringA(L"MyWideCharFileName"), ...);

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