iconv工具可正常音译带重音字符,但C++应用中iconv函数仅输出?的问题求助
iconv工具可正常音译带重音字符,但C++应用中iconv函数仅输出?的问题求助
我最近在写一个C++应用,遇到了个头疼的问题:我需要把LDAP数据库里的UTF-8编码数据输出到终端,但用户的终端可能用不同编码——比如ISO8859-1、CP-1252甚至ASCII。所以我想借助iconv()函数,把UTF-8转成用户会话locale对应的编码,尤其是当终端是ASCII时,用//TRANSLIT后缀把带重音的字符(比如ã、ç、É)转成不带重音的ASCII对应字符(a、c、E)。
在动手写代码之前,我先用iconv命令行工具测试了这个思路,结果完全符合预期(当时我的终端是UTF-8环境):
$ echo 'café com açúcar' café com açúcar $ echo 'café com açúcar' | iconv -f UTF-8 -t ASCII//TRANSLIT cafe com acucar $ echo 'café com açúcar' | iconv -f UTF-8 -t WCHAR_T | iconv -f WCHAR_T -t ASCII//TRANSLIT cafe com acucar
第三个命令里转成WCHAR_T的中间步骤,是我为了统计字符数(方便做屏幕格式化和补全,毕竟有多字节字符)想出来的方案,测试下来也没问题。
但轮到我自己写C++程序时,怪事发生了:所有带重音的字符转出来都是?,完全不是预期的无重音ASCII字符。我写了一个最小复现示例(加了点调试代码):
#include <iomanip> #include <iostream> #include <locale> #include <clocale> #include <cstdlib> #include <cstring> #include <cwchar> #include <iconv.h> using namespace std; const char msg[]="café com açúcar"; char local_msg[1000]; wchar_t wc_msg[1000]; // 调试用的字符串打印函数 void dump_str(const char *s){ cout << '"' << s << '"'; } void dump_str(const wchar_t *s){ cout << "L\"" << hex; while(*s) cout << "\\x" << setw(2) << setfill('0') << static_cast<wint_t>(*s++); cout << dec << '"'; } template <typename T, typename U, size_t Tz, size_t Uz> size_t debug_iconv(iconv_t &cvt, const T (&src)[Tz], size_t src_len, U (&dest)[Uz]){ char *in_str=reinterpret_cast<char *>(const_cast<T *>(src)); char *out_str=reinterpret_cast<char *>(dest); size_t in_left=src_len*sizeof *src; size_t out_left=Uz*sizeof *dest; int rc=iconv(cvt, &in_str, &in_left, &out_str, &out_left); cout << " iconv(...)=" << rc << "; src="; dump_str(src); cout << "; dst="; dump_str(dest); cout << "; in_left=" << in_left << "; out_left=" << out_left << '\n'; iconv(cvt, nullptr, nullptr, nullptr, nullptr); // 重置内部多字节转换状态 return src_len-in_left; } // 主函数,注释放开也没解决问题 int main(){ //setlocale(LC_ALL, ""); //locale::global(std::locale("")); // 从环境变量猜测终端的目标编码 const char *local_charset; if(const char *loc, *p; ((loc=getenv("LC_ALL")) || (loc=getenv("LC_CTYPE")) || (loc=getenv("LANG"))) && (p=strrchr(loc, '.')) && p>loc) local_charset=p+1; else local_charset="ASCII//TRANSLIT"; iconv_t u8_local=iconv_open(local_charset, "UTF-8"); iconv_t u8_wc=iconv_open("WCHAR_T", "UTF-8"); iconv_t wc_local=iconv_open(local_charset, "WCHAR_T"); cout << "UTF-8 -> " << local_charset << ":\n"; debug_iconv(u8_local, msg, strlen(msg), local_msg); memset(local_msg, 0, sizeof local_msg); cout << "UTF-8 -> WCHAR_T:\n"; debug_iconv(u8_wc, msg, strlen(msg), wc_msg); cout << "WCHAR_T -> " << local_charset << ":\n"; debug_iconv(wc_local, wc_msg, wcslen(wc_msg), local_msg); iconv_close(wc_local); iconv_close(u8_wc); iconv_close(u8_local); }
我运行这个示例的结果如下,不管是默认UTF-8会话还是强制用C locale,带重音的字符转出来都是?:
$ ./test UTF-8 -> UTF-8: iconv(...)=0; src="café com açúcar"; dst="café com açúcar"; in_left=0; out_left=982 UTF-8 -> WCHAR_T: iconv(...)=0; src="café com açúcar"; dst=L"\x63\x61\x66\xe9\x20\x63\x6f\x6d\x20\x61\xe7\xfa\x63\x61\x72"; in_left=0; out_left=3940 WCHAR_T -> UTF-8: iconv(...)=0; src=L"\x63\x61\x66\xe9\x20\x63\x6f\x6d\x20\x61\xe7\xfa\x63\x61\x72"; dst="café co?úcar"
(注:最后一行输出里,ç和ú对应的位置变成了?)
我实在搞不懂为什么命令行里的iconv可以正常工作,但自己写的C++程序就不行——有没有大佬能帮我看看问题出在哪?
内容来源于stack exchange
相关产品推荐
相关产品推荐

