[英]How to compare multibyte characters in C
我尝试解析文本并在其中找到一些字符。 我使用下面的代码。 它适用于诸如abcdef
普通字符,但不适用于öçşğüı
。 GCC会发出编译警告。 与öçşğüı
怎么öçşğüı
?
代码:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int main()
{
char * text = "öçşğü";
int i=0;
text = strdup(text);
while (text[i])
{
if(text[i] == 'ö')
{
printf("ö \n");
}
i++;
}
return 0;
}
警告 :
warning: multi-character character constant [-Wmultichar]
warning: comparison is always false due to limited range of data type [-Wtype-limits]
在while循环中打印char的地址时,有10个地址
printf("%d : %p \n", i, text[i]);
输出:
0 : 0xffffffc3
1 : 0xffffffb6
2 : 0xffffffc3
3 : 0xffffffa7
4 : 0xffffffc5
5 : 0xffffff9f
6 : 0xffffffc4
7 : 0xffffff9f
8 : 0xffffffc3
9 : 0xffffffbc
而strlen
是10
但是,如果我使用abcde
:
0 : 0x61
1 : 0x62
2 : 0x63
3 : 0x64
4 : 0x65
而strlen
是5。
如果我使用wchar_t
进行文本输出是
0 : 0xa7c3b6c3
1 : 0x9fc49fc5
2 : 0xbcc3
而strlen
是10, wcslen
是3。
要遍历字符串中的每个字符,可以使用mblen
。 您还需要设置正确的语言环境(由多字节字符串表示的编码),以便mblen
可以正确解析多字节字符串。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <locale.h>
int main()
{
char * text = "öçşğü";
int i=0, char_len;
setlocale(LC_CTYPE, "en_US.utf8");
while ((char_len = mblen(&text[i], MB_CUR_MAX)) > 0)
{
/* &text[i] contains multibyte character of length char_len */
if(memcmp(&text[i], "ö", char_len) == 0)
{
printf("ö \n");
}
i += char_len;
}
return 0;
}
字符串表示有两种类型,使用多字节(8位字节)或宽字节(大小取决于平台)。 多字节表示的优点是可以使用char *
(在代码中通常为c字符串)表示,但缺点是多个字节表示一个字符。 宽字符串使用wchar_t *
表示。 wchar_t
的优点是一个wchar_t是一个字符(但是,正如@anatolyg指出的那样,在wchar_t无法表示所有可能字符的平台上,这种假设仍然会出错)。
您是否使用十六进制编辑器查看了源代码? 字符串"öçşğü"
实际上由内存中的多字节字符串c3 b6 c3 a7 c5 9f c4 9f c3 bc
(UTF-8编码)表示,当然具有零终止。 您看到5个字符仅仅是因为该字符串已被您的UTF-8识别查看器/浏览器正确呈现。 很容易意识到strlen(text)
为此返回10,而上面的代码仅循环5次。
如果使用宽字节字符串,则可以按照@WillBriggs的说明进行操作。
没有标准可直接在源文件中嵌入非ASCII字符。
相反,C11标准指定您可以使用Unicode代码点:
wchar_t text[] = L"\u00f6\u00e7\u015f\u0131\u011f";
// Print whole string
wprintf(L"%s\n", text);
// Test individual characters
for (size_t i = 0; text[i]; ++i)
{
if ( text[i] == u'\u00f6' )
// whatever...
}
如果您使用的是Windows,那么您将面临一个额外的问题,即Windows控制台默认情况下无法打印Unicode字符。 您需要执行以下操作:
_setmode(1, _O_WTEXT);
,这将需要#include <fcntl.h>
。 之后,要恢复普通文本,您可以_setmode(1, _O_TEXT);
。
当然,如果要输出到文件或Win32 API函数,则无需执行这些步骤。
参见Wiki: https : //zh.wikipedia.org/wiki/UTF-8特别是,其中有一张带有位模式的表格。
这是将utf-8字符串扫描/转换为codepoint
的另一种方法[不完全是,只是一个示例-请参阅Wiki]:
// utf8scan -- convert utf8 to codepoints (example)
char inpbuf[1000];
char uni[8];
typedef union {
char utf8[4];
unsigned int code;
} codepoint_t;
codepoint_t outbuf[1000];
// unidecode -- decode utf8 char into codepoint
// RETURNS: updated rhs pointer
char *
unidecode(codepoint_t *lhs,char *rhs)
{
int idx;
int chr;
idx = 0;
lhs->utf8[idx++] = *rhs++;
for (; ; ++rhs, ++idx) {
chr = *rhs;
// end of string
if (chr == 0)
break;
// start of new ascii char
if ((chr & 0x80) == 0)
break;
// start of new unicode char
if (chr & 0x40)
break;
lhs->utf8[idx] = chr;
}
return rhs;
}
// main -- main program
int
main(void)
{
char *rhs;
codepoint_t *lhs;
rhs = inpbuf;
lhs = outbuf;
for (; *rhs != 0; ++lhs) {
lhs->code = 0;
// ascii char
if ((*rhs & 0x80) == 0)
lhs->utf8[0] = *rhs++;
// get/skip unicode char
else
rhs = unidecode(lhs,rhs);
}
// add EOS
lhs->code = 0;
return 0;
}
处理宽字符的最佳方法就是宽字符。
wchar_t myWord[] = L"Something";
这样做:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int main()
{
wchar_t * text = L"öçşğü";
int i = 0;
while (text[i])
{
if (text[i] == L'ö')
{
wprintf(L"ö \n");
}
i++;
}
return 0;
}
如果像我一样在Visual Studio中,请记住控制台窗口不能很好地处理Unicode。 您可以将其重定向到文件并检查文件,然后查看ö
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.