27

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;
}

奇妙なことは、ASCII 文字だけでなく@&@{čřžŧ¶'`[łĐŧđж←^€~[←^ø{&}čž、入力など、私が想像するあらゆる文字に対しても機能することです。

私の質問は - どのように可能ですか? これは C の多くの美しい例外の 1 つに思えますが、説明をいただければ幸いです。OS、コンパイラ、隠し言語の追加のスーパー機能の問題ですか?

ありがとう。

4

6 に答える 6

31

ここには魔法はありません。C 言語は、コンピュータのメモリに格納されている生のバイトにアクセスできます。端末が utf-8 を使用している場合 (その可能性が高い)、非 ASCII 文字はメモリ内で 1 バイト以上を使用します。もう一度表示すると、これらのシーケンスを単一の表示文字に変換する端末コードです。

文字列を出力するようにコードを変更するだけstrlenで、私の言いたいことがわかるでしょう。

C で utf-8 の非 ASCII 文字を適切に処理するには、glib、qt などのライブラリを使用して処理する必要があります。

于 2012-04-04T18:46:53.897 に答える
24

ASCIIは7ビットの文字セットです。Cでは通常8ビット文字で表されます。8ビットバイトの最上位ビットが設定されている場合、それはASCII文字ではありません。

また、ベースとしてASCIIが保証されていないことにも注意してください。多くの場合、他のシナリオは無視されます。「プリミティブ」バイトが英字であるかどうかを確認したい場合、言い換えれば、すべてのシステムに注意を払うときに、次のように言うことはできません。

is_alpha = (c > 0x40 && c < 0x5b) || (c > 0x60 && c < 0x7b);

代わりに、次のように使用ctype.hして言う必要があります。

isalpha(c);

唯一の例外であるAFAIKは数値用であり、少なくともほとんどのテーブルでは、連続した値があります。

したがって、これは機能します。

char ninec  = '9';
char eightc = '8';

int nine  = ninec  - '0';
int eight = eightc - '0';

printf("%d\n", nine);
printf("%d\n", eight);

ただし、これが「a」であるとは限りません。

alhpa_a = 0x61;

ASCIIに基づかない、つまりEBCDICを使用するシステム。このようなプラットフォームのCは引き続き正常に動作しますが、ここでは(ほとんどの場合)7ビットではなく8ビットを使用します。つまり、ASCIIではなくA10進数としてコーディングできます。19365


ただし、ASCIIの場合。10進数の128〜255(8ビット使用中)のバイトは拡張され、ASCIIセットの一部ではありません。つまり、ISO-8859はこの範囲を使用します。

よく行われること; また、2バイト以上を1文字に結合することもできます。したがって、たとえばutf8 0xc3 0x98 ==Øとして定義されている2バイトを次々に印刷すると、この文字が得られます。

これも、使用している環境によって異なります。多くのシステム/環境では、ASCII値を印刷すると、文字セットやシステムなどで同じ結果が得られます。ただし、127を超えるバイトまたは二重バイト文字を印刷すると、ローカル構成によって異なる結果が得られます。

すなわち:

プログラムを実行しているAさんは

Jasŋ€</p>

Bさんが

Jasπß

これはおそらく、拡張文字のシングルバイト表現などのISO-8859シリーズおよびWindows-1252に特に関係があります。


  • UTF-8#Codepage_layout、UTF-8にはASCIIがあり、次に特別なバイシーケンスがあります。
    • 各シーケンスは、127を超えるバイト(最後のASCIIバイト)で始まります。
    • その後に、すべてビットで始まる特定のバイト数が続きます10
    • つまり、マルチバイトUTF-8表現でASCIIバイトを見つけることは決してありません。

あれは; UTF-8の最初のバイトは、ASCIIでない場合、この文字のバイト数を示します。最上位ビットが0であるため、ASCII文字はこれ以上バイトが続かないと言うこともできます。

つまり、ファイルが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
...

例として。あなたが言及したキャラクターの1人を見ると。UTF-8端末の場合:

$echo-n"č"| xxd

降伏する必要があります:

0000000:c48d.。

言い換えると、「č」は2バイトの0xc4と0x8dで表されます。xxdコマンドに-bを追加すると、バイトのバイナリ表現が得られます。次のように分析します。

 ___  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

これにより、次のようになります。00100001101 2 = 269 10 =0x10D=>コードポイントU+010D=="č"のコードを解除します。

この番号は、HTMLでも&#269;==čとして使用できます。

これと他の多くのコードシステムに共通するのは、8ビットバイトがベースであるということです。


多くの場合、それは文脈についての質問でもあります。例として、ETSI GSM 03.38 / 03.40(3GPP TS 23.038、3GPP 23038 )を使用したGSMSMSを取り上げます。そこには、7ビットの文字テーブル、7ビットのGSMのデフォルトのアルファベットもありますが、それらを8ビットとして格納する代わりに、7ビット1として格納されます。このようにして、指定されたバイト数にさらに多くの文字をパックできます。つまり、標準のSMS 160文字は、ASCIIの場合は1280ビットまたは160バイトになり、SMSの場合は1120または140バイトになります。

1例外なくではありません(それは物語にもっとあります)。

つまり、SMS UDP形式でASCIIにセプテット(7ビット)C8329BFD06として保存されたバイトの簡単な例:

                                _________
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

そして、 「アンパック」された9バイトは10文字になります。

于 2012-04-04T18:58:18.910 に答える
6

ASCII は 8 ビットではなく 7 ビットです。achar []はバイトを保持します。これは、任意のエンコーディング (iso8859-1、utf-8 など) にすることができます。C 気にしない。

于 2012-04-04T18:45:55.987 に答える
2

非 ASCII 文字用のデータ型wint_t( ) があります。#include <wchar.h>メソッドgetwchar()を使用してそれらを読み取ることができます。

于 2012-04-04T18:48:53.593 に答える