c - UTF-8 字符计数在 C 中不正确
问题描述
我想计算给定的字符串计数(UTF-8),我可以得到输入格式是否以中文或希腊语或其他 UTF-8 字符串给出。
程序:
#include <stdio.h>
#define VAL_E0 0xE0
#define VAL_C0 0xC0
int UILexerCheckIsMultiByte(char *pchText , int nLength)
{
unsigned int nLen = nLength;
printf ("%s:%d pchText:%s nLen: %d \n", __FUNCTION__, __LINE__, pchText, nLen);
char *pchPtr = pchText;
int tmpVal = VAL_E0;
int nVal_C0 = VAL_C0;
int nByteCnt = 2;
int bIsfound = false;
while (nLen)
{
if ((pchText[nLen-1] & VAL_C0) == VAL_C0)
{
do
{
if ((pchText[nLen-1] & tmpVal) == nVal_C0)
{
bIsfound = true;
break;
}
nByteCnt++;
tmpVal = tmpVal >> 1 | 0x80;
nVal_C0= nVal_C0>> 1 | 0x80;
}while(tmpVal != 0xFF);
if (bIsfound)
break;
}
nLen--;
}
return nByteCnt;
}
int main()
{
if (setlocale(LC_ALL, "en_US.UTF-8") == NULL) {
abort();
}
char pchBuf[80] = "";
printf("\n Enter the character upto 20 in any form \n");
scanf("%[^\n]s",pchBuf);
int nLength=0;
int nMaxLen=20;
int nCharCnt = 0;
do
{
if (pchBuf[nLength]& 0x80)
{
int nByteCnt=0;
nByteCnt = UILexerCheckIsMultiByte(pchBuf, nMaxLen);
nLength += nByteCnt;
nCharCnt++;
}
else
{
nCharCnt++;
nLength++;
}
}
//while(nLength<nMaxLen);
while(pchBuf[nLength] != '\0');
printf ("CharCnt: %d \n", nCharCnt);
return 0;
}
样本输出:
-> ./a.out
Enter the character upto 20 in any form a擇擇cd UILexerCheckIsMultiByte:11 pchText:a擇擇cd nLen: 20 UILexerCheckIsMultiByte:11 pchText:a擇擇cd nLen: 20 CharCnt: 5 -> ./a.out Enter the character upto 20 in any form 中国话 不用彁 字。 UILexerCheckIsMultiByte:11 pchText:中国话 不用彁 字。 nLen: 20 UILexerCheckIsMultiByte:11 pchText:中国话 不用彁 字。 nLen: 20 UILexerCheckIsMultiByte:11 pchText:中国话 不用彁 字。 nLen: 20 UILexerCheckIsMultiByte:11 pchText:中国话 不用彁 字。 nLen: 20 UILexerCheckIsMultiByte:11 pchText:中国话 不用彁 字。 nLen: 20 UILexerCheckIsMultiByte:11 pchText:中国话 不用彁 字。 nLen: 20 UILexerCheckIsMultiByte:11 pchText:中国话 不用彁 字。 nLen: 20 UILexerCheckIsMultiByte:11 pchText:中国话 不用彁 字。 nLen: 20 CharCnt: 10 -> ./a.out Enter the character upto 20 in any form Янего UILexerCheckIsMultiByte:11 pchText:Янего nLen: 20 UILexerCheckIsMultiByte:11 pchText:Янего nLen: 20 UILexerCheckIsMultiByte:11 pchText:Янего nLen: 20 UILexerCheckIsMultiByte:11 pchText:Янего nLen: 20 UILexerCheckIsMultiByte:11 pchText:Янего nLen: 20 CharCnt: 5
当我使用混合 UTF-8 字符时,计数不正确。
-> ./a.out
Enter the character upto 20 in any form 用彁 Ĉĉ <==Chinese + roman UILexerCheckIsMultiByte:11 pchText:用彁 Ĉĉ nLen: 20 UILexerCheckIsMultiByte:11 pchText:用彁 Ĉĉ nLen: 20 UILexerCheckIsMultiByte:11 pchText:用彁 Ĉĉ nLen: 20 UILexerCheckIsMultiByte:11 pchText:用彁 Ĉĉ nLen: 20 UILexerCheckIsMultiByte:11 pchText:用彁 Ĉĉ nLen: 20 CharCnt: 6 -> ./a.out Enter the character upto 20 in any form 彁用 Αυ <==Chinese + Greek UILexerCheckIsMultiByte:11 pchText:彁用 Αυ nLen: 20 UILexerCheckIsMultiByte:11 pchText:彁用 Αυ nLen: 20 UILexerCheckIsMultiByte:11 pchText:彁用 Αυ nLen: 20 UILexerCheckIsMultiByte:11 pchText:彁用 Αυ nLen: 20 UILexerCheckIsMultiByte:11 pchText:彁用 Αυ nLen: 20 CharCnt: 6
我需要做哪些修改才能获得正确的字符数?
解决方案
主要问题在于 UILexerCheckIsMultiByte。要解码 utf8 流,您需要查看每个字节的前(最高)2 位。如果它们是“01”它是一个8位字符代码,如果它们是“11”它是一个多字节序列的第一个字节,如果它们是“10”它是一个多字节中的一个字节-序列。
您的第一个二进制比较是正确的: (ch & 0xC0) == 0xC0 - 这将屏蔽前两位并检查模式“11xxxxxx”(x 表示不关心)
但你的下一个比较是错误的。在第一次运行中,您检查: (ch & 0xE0) == 0xC0 - 这将屏蔽前三位并检查模式“11xxxxxx”,但您应该检查“10xxxxxx”。
所以也许你看看下面的代码:有两个版本的 strlen 和一个函数来计算多字节序列的字节数。
/* gcc -Wall -o strlen strlen.c */
#include "stdio.h"
int utf8charsize(char *s)
{
int cnt=0;
if( *s ) {
cnt++;
if( (*s & 0xc0) == 0x0c0 ) { /* binary is 11xxxxxx */
while( (s[cnt] & 0xc0) == 0x80 ) /* binary code is 10xxxxxx */
cnt++;
}
}
printf("-- %d\n", cnt );
return cnt;
}
int utf8strlen(char *s)
{
int cnt=0;
int clen;
while(*s) {
clen=utf8charsize(s);
cnt++;
s+=clen;
}
return cnt;
}
int utf8strlen2(char *s)
{
int cnt=0;
while(*s) {
cnt++;
if( (*s++ & 0xc0) == 0x0c0 ) { /* binary is 11xxxxxx */
while( (*s & 0xc0) == 0x80 ) /* binary code is 10xxxxxx */
s++;
}
}
return cnt;
}
int main(int argc, char **argv)
{
if( argc > 1 )
printf("%d %d\n", utf8strlen(argv[1]), utf8strlen2(argv[1]));
return 0;
}
推荐阅读
- node.js - 通过 Shopify ScriptTag API 读取产品元字段
- sql - 现有索引计数是否会影响索引创建时间?
- javascript - 有没有办法在键盘事件中检索按下的键的显示值
- jenkins - 从 .txt 文件中提取一行并将其粘贴到 Jenkins email-ext Default-Content 字段中
- r - 如何使用数字选择数据框的列
- google-apps-script - Using authorization token from G Suite add-on for use on my api service
- c - can't figure out the sizeof(long double) in C is 16 bytes or 10 bytes
- amazon-web-services - S3 GetObjectTagging error for only subset of similar files
- android - Android FastAdapter ClickEventHook isn't working properly
- c# - HTTP Error 500.0 - ANCM In-Process Handler Load Failure