我现在正在Linux上学习C语言,我遇到了一些奇怪的情况.
据我所知,标准C的char数据类型是ASCII,1字节(8位).它应该意味着它只能包含ASCII字符.
在我使用的程序中char input[],它由getchar像这个伪代码的函数填充:
char input[20];
int z, i;
for(i = 0; i < 20; i++)
{
z = getchar();
input[i] = z;
}
Run Code Online (Sandbox Code Playgroud)
奇怪的是它不仅适用于ASCII字符,而且适用于我想象的任何字符,例如@&@{??ž?¶'`[?????¶?^€~[?^ø{&}?ž输入.
我的问题是 - 怎么可能?它似乎是C中许多美丽的例外之一,但我真的很感激解释.这是操作系统,编译器,隐藏语言的附加超级功能吗?
谢谢.
jsb*_*eno 22
这里没有什么魔力 - C语言让你可以访问原始字节,因为它们存储在comptuer内存中.如果您的终端使用utf-8(很可能),非ASCII字符在内存中占用多个字节.再次显示时,我们的终端代码将这些序列转换为单个显示的字符.
只需更改代码即可打印strlen字符串,您将看到我的意思.
要在C中正确处理utf-8非ASCII字符,你必须使用一些库来为你处理它们,比如glib,qt或许多其他字符.
Mor*_*pfh 17
ASCII是一个7位字符集.在C中通常由8位字符表示.如果设置了8位字节中的最高位,则它不是 ASCII字符.
另请注意,您不能保证ASCII为基础,许多人忽略其他方案.如果你想检查"原始"字节是否是字母字符,你可以换句话说,在注意所有系统时,请说:
is_alpha = (c > 0x40 && c < 0x5b) || (c > 0x60 && c < 0x7b);
Run Code Online (Sandbox Code Playgroud)
相反,你必须使用ctype.h并说:
isalpha(c);
Run Code Online (Sandbox Code Playgroud)
唯一的例外,AFAIK,对于数字,至少在大多数表上,它们具有连续的值.
这样可行;
char ninec = '9';
char eightc = '8';
int nine = ninec - '0';
int eight = eightc - '0';
printf("%d\n", nine);
printf("%d\n", eight);
Run Code Online (Sandbox Code Playgroud)
但这不能保证是'a':
alhpa_a = 0x61;
Run Code Online (Sandbox Code Playgroud)
不基于ASCII的系统,即使用EBCDIC ; 在这样的平台上的C仍然运行良好,但在这里它们(大多数)使用8位而不是7位,即A可以编码为十进制193而不是65ASCII.
但是对于ASCII; 具有十进制128 - 255(使用中的8位)的字节被扩展,而不是ASCII集的一部分.即ISO-8859使用此范围.
经常做什么; 也是将两个或多个字节组合成一个字符.因此,如果你打印两个字节后,定义为utf8 0xc3 0x98 ==Ø,那么你将得到这个字符.
这又取决于您所处的环境.在许多系统/环境中,打印ASCII值会在字符集,系统等中产生相同的结果.但打印字节> 127或双字节字符会根据本地配置给出不同的结果.
即:
A先生正在执行该程序
JASN€
而B先生得到了
Jasπß
这可能与ISO-8859系列和扩展字符的单字节表示的Windows-1252特别相关.
10.那是; UTF-8中的第一个字节,如果不是ASCII,则表示该字符有多少字节.您也可以说ASCII字符表示不再有字节 - 因为最高位为0.
即如果文件被解释为UTF-8:
fgetc(c);
if c < 128, 0x80, then ASCII
if c == 194, 0xC2, then one more byte follow, interpret to symbol
if c == 226, 0xE2, then two more byte follows, interpret to symbol
...
Run Code Online (Sandbox Code Playgroud)
举个例子.如果我们看一下你提到的一个角色.如果在UTF-8终端:
$ echo -n"č"| XXD
应该产量:
0000000:c48d ..
换句话说,"č"由两个字节0xc4和0x8d表示.将-b添加到xxd命令,我们得到字节的二进制表示.我们按如下方式剖析它们:
___ byte 1 ___ ___ byte 2 ___
| | | |
0xc4 : 1100 0100 0x8d : 1000 1101
| |
| +-- all "follow" bytes starts with 10, rest: 00 1101
|
+ 11 -> 2 bits set = two byte symbol, the "bits set" sequence
end with 0. (here 3 bits are used 110) : rest 0 0100
Rest bits combined: xxx0 0100 xx00 1101 => 00100001101
\____/ \_____/
| |
| +--- From last byte
+------------ From first byte
Run Code Online (Sandbox Code Playgroud)
这给我们:00100001101 2 = 269 10 = 0x10D =>解码码点U + 010D =="č".
此数字也可以在HTML中用作č==č
这个以及许多其他代码系统的共同点是8位字节是基础.
通常它也是关于背景的问题.作为一个例子取GSM SMS,与ETSI GSM 03.38/03.40(3GPP TS 23.038,3GPP 23038).在那里我们还找到了一个7位字符表,7位GSM默认字母表,但不是将它们存储为8位,而是存储为7位1.这样,您可以将更多字符打包到给定的字节数中.即标准SMS 160字符变为1280位或160字节作为ASCII和1120或140字节作为SMS.
1不例外,(故事更多).
即一个简单的字节示例,以SMS UDP格式保存为septets(7bit)C8329BFD06到ASCII:
_________
7 bit UDP represented | +--- Alphas has same bits as ASCII
as 8 bit hex '0.......'
C8329BFDBEBEE56C32 1100100 d * Prev last 6 bits + pp 1
| | | | | | | | +- 00 110010 -> 1101100 l * Prev last 7 bits
| | | | | | | +--- 0 1101100 -> 1110010 r * Prev 7 + 0 bits
| | | | | | +----- 1110010 1 -> 1101111 o * Last 1 + prev 6
| | | | | +------- 101111 10 -> 1010111 W * Last 2 + prev 5
| | | | +--------- 10111 110 -> 1101111 o * Last 3 + prev 4
| | | +----------- 1111 1101 -> 1101100 l * Last 4 + prev 3
| | +------------- 100 11011 -> 1101100 l * Last 5 + prev 2
| +--------------- 00 110010 -> 1100101 e * Last 6 + prev 1
+----------------- 1 1001000 -> 1001000 H * Last 7 bits
'------'
|
+----- GSM Table as binary
Run Code Online (Sandbox Code Playgroud)
9个字节"unpacked"变为10个字符.
小智 7
当然有很多库可以完成这项工作,但是要快速解码任何 UTF8 unicode,这个小函数很方便:
typedef unsigned char utf8_t;
#define isunicode(c) (((c)&0xc0)==0xc0)
int utf8_decode(const char *str,int *i) {
const utf8_t *s = (const utf8_t *)str; // Use unsigned chars
int u = *s,l = 1;
if(isunicode(u)) {
int a = (u&0x20)? ((u&0x10)? ((u&0x08)? ((u&0x04)? 6 : 5) : 4) : 3) : 2;
if(a<6 || !(u&0x02)) {
int b,p = 0;
u = ((u<<(a+1))&0xff)>>(a+1);
for(b=1; b<a; ++b)
u = (u<<6)|(s[l++]&0x3f);
}
}
if(i) *i += l;
return u;
}
Run Code Online (Sandbox Code Playgroud)
考虑你的代码;您可以迭代字符串并读取 unicode 值:
int l;
for(i=0; i<20 && input[i]!='\0'; ) {
if(!isunicode(input[i])) i++;
else {
l = 0;
z = utf8_decode(&input[i],&l);
printf("Unicode value at %d is U+%04X and it\'s %d bytes.\n",i,z,l);
i += l;
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
26478 次 |
| 最近记录: |