逐个字符访问外语字符串

Tho*_*den 9 c string unicode-string

我知道这个问题可能非常基础.如果这是显而易见的事,请原谅我.考虑以下程序:

#include <stdio.h>

int main(void) {
   // this is a string in English
   char * str_1 = "This is a string.";
   // this is a string in Russian
   char * str_2 = "??? ????????? ?????????.";
   // iterator
   int i;
   // print English string as a string
   printf("%s\n", str_1);
   // print English string byte by byte
   for(i = 0; str_1[i] != '\0'; i++) {
      printf(" %c  ",(char) str_1[i]);
   }
   printf("\n");
   // print numerical values of English string byte by byte
   for(i = 0; str_1[i] != '\0'; i++) {
      printf("%03d ",(int) str_1[i]);
   }
   printf("\n");
   // print Russian string as a string
   printf("%s\n", str_2);
   // print Russian string byte by byte
   for(i = 0; str_2[i] != '\0'; i++) {
      printf(" %c  ",(char) str_2[i]);
   }
   printf("\n");
   // print numerical values of Russian string byte by byte
   for(i = 0; str_2[i] != '\0'; i++) {
      printf("%03d ",(int) str_2[i]);
   }
   printf("\n");
   return(0);
}
Run Code Online (Sandbox Code Playgroud)

输出:

This is a string.
 T   h   i   s       i   s       a       s   t   r   i   n   g   .
084 104 105 115 032 105 115 032 097 032 115 116 114 105 110 103 046
??? ????????? ?????????.
 ?   ?   ?   ?   ?   ?       ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?       ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   .
-48 -83 -47 -126 -48 -66 032 -47 -127 -47 -126 -47 -128 -48 -66 -48 -70 -48 -66 -48 -78 -48 -80 -47 -113 032 -48 -70 -48 -66 -48 -67 -47 -127 -47 -126 -48 -80 -48 -67 -47 -126 -48 -80 046
Run Code Online (Sandbox Code Playgroud)

可以看出,英文(ASCII)字符串可以作为字符串打印或使用数组索引访问并逐字符(逐字节)打印,但俄语字符串(我相信编码为UTF-8)可以打印为一个字符串,但不是按字符访问.

据我所知,原因是在这种情况下俄语字符使用两个字节而不是一个字节进行编码.

我想知道的是,是否有任何简单的方法可以通过正确的数据类型声明或通过某种方式标记字符串或通过设置来使用标准C库函数逐字符打印Unicode字符串(在本例中为两个字节乘两个字节)区域设置或其他方式.

我尝试用"u8"在俄语字符串前面,也就是说char * str_2 = u8"...",这不会改变行为.我想远离使用宽字符来假设使用什么语言,例如每个字符恰好两个字节.任何意见,将不胜感激.

Jon*_*ler 3

我认为mblen()mbtowc()wctomb()mbstowcs()wcstombs()函数<stdlib.h>部分相关。mblen()例如,您可以使用 找出字符串中每个字符由多少个字节组成。

\n\n

另一个很少使用的标头和函数是<locale.h>and setlocale()

\n\n

这是您的代码的改编:

\n\n
#include <assert.h>\n#include <locale.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\nstatic inline void ntbs_hex_dump(const char *pc_ntbs)\n{\n    unsigned char *ntbs = (unsigned char *)pc_ntbs;\n    for (int i = 0; ntbs[i] != \'\\0\'; i++)\n        printf(" %.2X ", ntbs[i]);\n    putchar(\'\\n\');\n}\n\nstatic inline void ntbs_chr_dump(const char *pc_ntbs)\n{\n    unsigned char *ntbs = (unsigned char *)pc_ntbs;\n    for (int i = 0; ntbs[i] != \'\\0\'; i++)\n        printf(" %c  ", ntbs[i]);\n    putchar(\'\\n\');\n}\n\nint main(void)\n{\n    char *loc = setlocale(LC_ALL, "");\n    printf("Locale: %s\\n", loc);\n\n    char *str_1 = "This is a string.";\n    char *str_2 = "\xd0\xad\xd1\x82\xd0\xbe \xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xba\xd0\xbe\xd0\xb2\xd0\xb0\xd1\x8f \xd0\xba\xd0\xbe\xd0\xbd\xd1\x81\xd1\x82\xd0\xb0\xd0\xbd\xd1\x82\xd0\xb0.";\n\n    printf("English:\\n");\n    printf("%s\\n", str_1);\n    ntbs_chr_dump(str_1);\n    ntbs_hex_dump(str_1);\n\n    printf("Russian:\\n");\n    printf("%s\\n", str_2);\n    ntbs_chr_dump(str_2);\n    ntbs_hex_dump(str_2);\n\n    char *mbp = str_2;\n    while (*mbp != \'\\0\')\n    {\n        enum { MBS_LEN = 10 };\n        int mbl = mblen(mbp, strlen(mbp));\n        char mbs[MBS_LEN];\n        assert(mbl < MBS_LEN - 1 && mbl > 0);\n        // printf("mbl = %d\\n", mbl);\n        memmove(mbs, mbp, mbl);\n        mbs[mbl] = \'\\0\';\n        printf(" %s ", mbs);\n        mbp += mbl;\n    }\n    putchar(\'\\n\');\n\n    return(0);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

setlocale()很重要,至少在 macOS Sierra 10.12.2(带有 GCC 6.3.0)上,这是我开发和测试它的地方。如果没有它,mblen()总是返回1,并且代码中没有任何好处。

\n\n

我从中得到的输出是:

\n\n
Locale: en_US.UTF-8\nEnglish:\nThis is a string.\n T   h   i   s       i   s       a       s   t   r   i   n   g   .  \n 54  68  69  73  20  69  73  20  61  20  73  74  72  69  6E  67  2E \nRussian:\n\xd0\xad\xd1\x82\xd0\xbe \xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xba\xd0\xbe\xd0\xb2\xd0\xb0\xd1\x8f \xd0\xba\xd0\xbe\xd0\xbd\xd1\x81\xd1\x82\xd0\xb0\xd0\xbd\xd1\x82\xd0\xb0.\n ?   ?   ?   ?   ?   ?       ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?       ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   .  \n D0  AD  D1  82  D0  BE  20  D1  81  D1  82  D1  80  D0  BE  D0  BA  D0  BE  D0  B2  D0  B0  D1  8F  20  D0  BA  D0  BE  D0  BD  D1  81  D1  82  D0  B0  D0  BD  D1  82  D0  B0  2E \n \xd0\xad  \xd1\x82  \xd0\xbe     \xd1\x81  \xd1\x82  \xd1\x80  \xd0\xbe  \xd0\xba  \xd0\xbe  \xd0\xb2  \xd0\xb0  \xd1\x8f     \xd0\xba  \xd0\xbe  \xd0\xbd  \xd1\x81  \xd1\x82  \xd0\xb0  \xd0\xbd  \xd1\x82  \xd0\xb0  . \n
Run Code Online (Sandbox Code Playgroud)\n\n

稍加努力,代码就可以将 UTF-8 数据的字节对打印得更紧密。D0 和 D1 前导字节对于 BMP(基本多语言平面)中西里尔字母代码块 U+0400 .. U+04FF 的 UTF-8 编码是正确的。

\n\n

只是为了您的娱乐价值:BSDsed拒绝处理输出,因为这些问号代表无效代码:sed: RE error: illegal byte sequence

\n