Количество символов UTF-8 не в порядке в C - PullRequest
0 голосов
/ 03 мая 2018

Я хочу посчитать заданное количество строк (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

Какие модификации мне нужно сделать, чтобы получить правильное количество символов?

1 Ответ

0 голосов
/ 06 мая 2018

Основная проблема в UILexerCheckIsMultiByte. Для декодирования потоков utf8 вам нужно посмотреть на первых (старших) 2 битах каждого байта. если они "01", это 8-битный код символа, если они равны «11», то это первый байт многобайтовой последовательности, если они равны 10, это один байт внутри многобайтовой последовательности.

Ваше первое двоичное сравнение корректно: (ch & 0xC0) == 0xC0 - это замаскирует первые два бита и проверит наличие шаблона "11xxxxxx" (x означает, что все равно)

но ваше следующее сравнение неверно. при первом запуске вы проверяете: (ch & 0xE0) == 0xC0 - это замаскирует первые три бита и проверит наличие шаблона «11xxxxx», но вы должны проверить «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;                                            
     }                                                                                
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...