Comments
Description
Transcript
文字、文字列、文字配列とポインタ
文字、文字列、文字配列とポインタ 「文字」と「整数」 プログラム中では、 「文字」は char で扱います。8bit の整数です。 今日のテーマは、画面に表示される「文字」が、内部では数字と表 現されているということ。そして、char に数字(文字コード)を代 入して、それを表示すると「文字」になることの確認です。 ということは、「画面に表示されているこの文字は、こんなコ ードであることを確認した。」とか、 「この文字コードを表示し たら、こんな文字が画面に表示された。」というレポートのま とめ方をしてくれたなら、基本的な点数が出せます。 C 言語は、英語を元に設計されていますから、 char で表現できる 範囲に「日本語の文字」は含まれていません。このため、実用のプ ログラムでは様々な発展形が工夫されていて、 例えば String など では日本語の「文字」を扱えるような仕掛けが組み込まれています。 しかし、まずは「英語」ベースのコンピュータ環境ということで、 アルファベット文字を扱う char を覚えましょう。 char は、character (文字)の先頭部分です。通常は「キャラ」と読みますが、 「チャラ」と読む人も中にはいます。 char は、1バイトで格納されます。Int は、通常8バイト(64 ビット)で格 納されます(昔の規格では 4 バイト;32bit でしたが、現在は int と宣言する と 8 バイトになります)から、int に比べると、かなりメモリの節約になります。 但し、28 = 256 (0〜255)の範囲しか表現できません。つまり、文字の数が、 最大でも 256 文字しかない国で、この言語は発達したのです。その国での、 「す べての文字」をコンピュータ用のコードで表現したのが、ASCII コード表です。 1 IT 用語辞典(http://e-words.jp/p/r-ascii.html) 文 10 16 文 10 16 文 10 16 文 10 16 文 10 16 文 10 16 文 10 16 文 10 16 字 進進 字 進進 字 進進 字進進 字 進進 字 進進 字 進 進 字 進 進 NUL 0 00 DLE 16 10 SP 32 20 0 48 30 @ 64 40 P 80 50 ` 96 60 p 112 70 SOH 1 01 DC1 17 11 ! 33 21 1 49 31 A 65 41 Q 81 51 a 97 61 q 113 71 STX 2 02 DC2 18 12 " 34 22 2 50 32 B 66 42 R 82 52 b 98 62 r 114 72 ETX 3 03 DC3 19 13 # 35 23 3 51 33 C 67 43 S 83 53 c 99 63 s 115 73 EOT 4 04 DC4 20 14 $ 36 24 4 52 34 D 68 44 T 84 54 d 100 64 t 116 74 ENQ 5 05 NAK 21 15 % 37 25 5 53 35 E 69 45 U 85 55 e 101 65 u 117 75 ACK 6 06 SYN 22 16 & 38 26 6 54 36 F 70 46 V 86 56 f 102 66 v 118 76 BEL 7 07 ETB 23 17 ' 39 27 7 55 37 G 71 47 W 87 57 g 103 67 w 119 77 BS 8 08 CAN 24 18 ( 40 28 8 56 38 H 72 48 X 88 58 h 104 68 x 120 78 HT 9 09 EM 41 29 9 57 39 I 25 19 ) 73 49 Y 89 59 i 105 69 y 121 79 74 4a Z 90 5a j 106 6a z 122 7a 91 5b k 107 6b { 123 7b LF* 10 0a SUB 26 1a * 42 2a : 58 3a J VT 59 3b K 75 4b [ 11 0b ESC 27 1b + 43 2b ; FF* 12 0c FS 28 1c , 44 2c < 60 3c L 76 4c ¥¥ 92 5c l 108 6c | 124 7c CR 13 0d GS 29 1d - 45 2d = 61 3d M 77 4d ] 93 5d m 109 6d } 125 7d SO 14 0e RS 30 1e . 46 2e > 62 3e N 78 4e ^ 94 5e n 110 6e ~ 126 7e SI 15 0f US 31 1f / 47 2f ? 63 3f O 79 4f _ 95 5f o 111 6f DEL 127 7f ASCII は、American Standard Code for Information Interchange です。 日本語の文字を C 言語の環境で扱う場合には、2バイトを使います。 コンピュータが普及する初期の頃は、1バイトのコード表の領域に、無理矢理 に、日本語のカタカナや日本語文字を割り振りました。このため、「カタカナ」 だけは「半角の文字(1バイト文字)」があります。 (WEB アプリでは誤動作の 原因となるため、使わせない方向になっています。) その後、すべての日本語文字を2バイトで定義して、そのコード領域に改めて 英数字やカタカナも定義したため、「英数字、カタカナ」には、「半角文字」と 「全角文字」ができてしまう、ということになってしまいました。 どの「数値」をどの文字に当てはめるか、という数表を「文字コード表」と言 います。 2 日本語の文字コードでは、現在は主には三つのコード表が使われています。 Shift-JIS : Microsoft の Windows を中心に使われています。 Macintosh は、最初は別のコードを使っていましたが、現在は、 Shift-JIS を使うようになりました。 UTF-8: (Unicode と呼ばれるコード表)のうち、現在、WEB(Internet) で多く使われているコード表です。 EUC: Enhanced Unix Code(拡張 UNIX コード)の略称通り、UNIX や UNIX の流れを持つ LINUX で使われている文字コードです。 LINUX コンピュータは、WEB などの「ホスト」 (ネット上で処理を 実際に行っているコンピュータ)で多く使われています。 この三つが混在しているため、しばしば「文字化け」と呼ばれる現象が起こり ます。 ASCII の名称や、 「日本語文字コード」の種類などについては、臨床工学の国 家試験の「情報工学」の範囲にも含まれていますので、覚えておきましょう。 ‘(単一引用符号; Single quotation) ”(二重引用符号; double quotation) 文字を表現する際には、’ (single quotation)を用い、 「文字列」を表現する 際には”(double quotation)を使います。 実は、これらは先週の「RPG」のサンプルプログラムの中に登場しました。 「スライム」などは “スライム“として出て来ました。 また、 「メッセージ」をプログラム中に書き込む際には、いつも”二重引用符 号で囲んできました。 これが一つだけ(単一)の引用符号になった時には、「文字」を表します。 登場したのは、「画面からの文字入力」です。一文字だけを取得するために、 x = _getch(); という関数を使い、 if( x == '1' ){ ... } 入力のあった「文字」を比較するために、x == ‘1’という書き方をしていま 3 すが、この右側が文字「1」を意味しています。 文字の「1」と、数値の「1」とは異なります。 「文字」の「1」にはコード表 の値が割り振られているため、全然違う値を持っています。それが ASCII コー ド表に書かれています。文字の「1」は、コードの値(数値)として 49 であ ることがわかります。 この「文字」を printf 文で画面に表示する際には、%c で表示します。 %c の c は、char の c です。 同様に、 「文字列」%s の s は、string の s です。 今日の例題(1)コンソールプログラム コンソールプログラムとして、以下のプログラムを書いてください。 (貼付けても構いませんが、打ち込んだ方が練習にはなります。授 業では、実演します。) int _tmain(int argc, _TCHAR* argv[]) { char str[256]; int pos = 0; printf("入力文字列 >> "); gets_s(str); while (str[pos]){ printf("%2d - [%2X:%3d], %c¥n", pos, str[pos], str[pos], str[pos]); pos++; Sleep(500); } printf("[Enter]を入力(終了) >> "); gets_s(str); return 0; } Sleep()を使いましたので、<windows.h>を include しておいて下さい。 _tmain は一つだけです。既に自動生成されている中に書き込んで下さい。 0.5 秒ごとに、入力された「文字列」の中の文字を一つずつ表示するプログラ ムです。実行すると以下のようになります。 4 最初に、gets( str ); で、画面から文字列を入力します。 gets で「文字列」を読み込むのは、「配列」と言います。 配列とは何か? 一つの名前に対して複数のデータを連続的に格納できる領域のことです。アパ ートのようなものだと思ってください。 配列がこの授業で初登場したのは、「数当てゲーム」です。3桁の数 字を、順番に、ループで処理しながら扱う時に登場しました。 例えば、1〜3桁目の「答え」を作った部分は for( i=0; i<3; i++ ){ num = rand() % 10; ans[i] = num; } でした。 アパートのそれぞれの部屋を区別する際には [ 番号 ] と書きます。カギ括弧 です。続き番号は、0から始まりますので、3個の部屋がある時には 0 号室 、 1 号室、2 号室となります。 文字列の場合には、0 号室から順番に並べて、それぞれの中身の文字だけをつ なげて読むと、一つの「文字列」になります。それぞれの中身を連続的に読み 出して行きます。 5 「文字列の終わり」は、NULL(空文字)です。 コード番号 0 の文字は、いわゆる「空白」 (スペース=32)とは区別して、ヌル コード(Null Code)と言います。ヌルとかナルとか読みます。 while( str[pos] ){ . . . } は、str[pos] がゼロでない間、(null でない間)は処理を続ける、という意 味があり、ループの最後で pos ++ でインクリメントしていますから、pos の 値を一つずつ増やしながら、この「文字列アパート」の住人( str[pos] )を 順番に訪ねていることになります。 printf( "%2d - [%2X:%3d], %c¥n", pos, str[pos], str[pos], str[pos] ); この行では、 str[pos]を3通りで(3回呼び出して)表示しています。 最初の %2X は、16進数 2 桁で表示するという意味で、次の %3d では、10進 数 3 桁で表示します。最後の %c は「文字」として表示する、という意味で す。 つまり、これで見ると ‘A’という文字は、16進数だと41、10進数だと65で、入力があった 文字列の「配列」では、0号室(最初)に入居 している、 ということがわかります。 ここで、試しにプログラムに漢字を入力してみて ください。「あいうえお。漢字」という8文字に 対して、部屋が 16 個埋まっています。 これらは、「2バイトコード」として、1文字が 2バイトで表現されています。 表示が変です。16進数は2桁で表示されるはず なのですが、8桁(64 ビット分)が割り振られています。 実は、理由があります。char と宣言すると8バイトの数字が-128〜127 とい う範囲に割り振られて、80 以上の値は内部的にはマイナスとして処理されます。 このため、「先行する6桁分」に、F(二進数の 1111)が埋められています。 気持ちが悪いので、直しましょう。 6 printf( "%2d - [%2X:%3d], %c\n", pos, (unsigned char)str[pos], str[pos], str[pos] ); %2X に対応する2番目の引数が、符号なしの char (0〜255)だ、ということを明確にするた めに、(unsigned char)という「符号なしの文 字」で扱うように宣言します。このように、強 制的に型を変換することを「キャストする」 と言います。 これで漢字を表示させてみましょう。 無事、16 進数が2桁で表示されました。 それにしても、2バイト文字を1文字ずつ処理 すると、まったく文字に見えませんね。こうし た「化け方」もあります。 このため、漢字をあつかったりする場合には、char 型しか使えない C 言語では なく、String 型を使える C++や C#(オブジェクト指向の C 言語)環境の方が、 プログラムは楽になります。 実用的には、原理は C 言語で学び、実際に使うノウハウは C++や C#で学ぶ、 というのが良いでしょう。皆さんの言語環境は、C++も使えますから(というよ りも、完全な C 言語は使いませんし、使っていませんから)積極的に漢字など の処理方法を学んで行きましょう。 7 ポインタ演算子 今度は、プログラムを以下のように書き換えます。 char str[256]; char *ptr; printf( "入力文字列 >> " ); gets_s( str ); ptr = str; while( *ptr ){ printf( "[%2X:%3d], %c¥n", (unsigned char)*ptr, *ptr, *ptr ); ptr++; Sleep( 500 ); } printf( "[Enter]を入力(終了) >> " ); gets_s( str ); return 0; ------------- どこが変わったか?ですが、住所を表す pos のことを配列の「添字」と言いま すが、これがなくなって、ptr で直接 str をさすことにしました。 最初に、ptr に str を代入しています。str[0] と書くと、これは配列の「0」 号室の中身の値をさしていますが、str 単独だと、0号室そのものを表していま す。 ptr は、中身ではなく str がさしている 0 号室を指していて、これを「ポ インタ」と言います。 ptr が指している 0 号室の中身の値を見る時には、* (asterisk; 星印;アスタリスク)をつけて、 *ptr と記します。 このため、 while( *ptr ){ の、ループの反復条件は、ptr の指す配列の中身が空でない、ということを意 味していています。 ptr++; で、一つずつ隣を指すように部屋番号を増やしながら、 printf( "[%2X:%3d], %c¥n", (unsigned char)*ptr, *ptr, *ptr ); で順番に中身を表示していく、という処理を行っていることになります。 8 ポインタ演算子 * は、かなり難しいです。初心者の平均的な学生が半年(3 ヶ月)程度の演習で理解しようとしても、まず雰囲気をつかむのが精一杯です。 ポインタも今後の演習では使っていきますが、 「プログラムは、大学 2 年の演習 の時に習っただけで、全然仕事でも使わなかった」という人が大半だと思うの で、軽く流しましょう。 (将来、情報系に進みたい人は、非常に重要な概念です ので絶対にスルーせずに、完全に理解するまで考えてみて下さい。逆に、これ を理解すると、アドレス管理の全体構造が見えて来て、一気に中級レベルのプ ログラマになれます。) 【報告課題(1)】 まず、 「例題(1)」のプログラムを入力し、実行させて下さい。 「記号」、 「アルファベット大文字」 「アルファベット小文字」を入力した時に、 文字コードがコード表に記された値と同じであることを確認してください。 プログラムの実行画面とコーソがまず添付されていて、その上で、16 進数の 値と 10 進数の値(コード)と、文字が一致することを、コード表のその部分 を赤丸で囲むなどして、示して下さい。 (ここまでで、2 点) 【報告課題(2)】 漢字を入力して、入力した漢字と、その漢字のシフト JIS の文字コードが一 致することを、シフト JIS の文字コード表から探して、確認してください。 文字列は、3文字程度で、何でも構いません。 プログラムの実行画面の他に、ネットなどで検索したシフト JIS の文字コー ド表の、入力した漢字の部分の画面コピーなどを一緒につけて、16 進数での表 記か、または、10 進数での表記か、いずれかの文字コードと、コード表の結果 で、文字コードに対応する文字が、その入力された文字であることを示してく ださい。 (1文字について、コード表で確認が取れれば OK とします。) (この課題単独で2点:ここまでで、4 点) 9 【例題プログラム(3)】 以下のプログラムを実行させて下さい。 int _tmain(int argc, _TCHAR* argv[]) { char str[256] = { -109, 110, -107, -45, -106, -125, -105, 70, 0 }; printf("%s¥n", str); printf("[Enter]を入力(終了) >> "); gets_s(str); return 0; } 数字が大事です。間違えないで下さい。どんな表示になりましたか?(それは 見てのお楽しみ。) 【報告課題(3)】 上記の例題を作り替えて、自分の名前を表示させて下さい。 (どうしても、自分の名前がイヤだという人は、別の名前でも構いませんが、 他の人がプログラムで表示させた名前はカブった人全員を×にするので、カブ らないようにするには自分の名前が一番確実です。) プログラムリストと画面コピーをつけて、 「どの文字が、どんな文字コードなの か」を報告して下さい。 文字コードを説明する場合は、文字コード表を画面表示させて、表示されてい る文字の部分を切り取ってレポートに貼付け、なぜその数字だったのかを説明 して下さい。 この時に、必要ならば 16 進数を 10 進数(マイナスの数は補数表示)に書き 換えて下さい。 (この課題単独で、4 点。ここまで、A 評価;8 点:報告に不備があれば減点し ます。) このままだと、レポート作成が大変です。授業中にコード表の読み方と、16 進 数の計算、2の補数計算の説明を行います。が、ボーナス課題として 2 の補数 についての説明と、この課題での計算例の報告があった場合には 2 点を加点し ます。 10 【今日最後の例題プログラム】 今日の最後の例題プログラムは、フォームで作ります。 「お楽しみ」ですが、最初に何を作るか言ってしまいましょう。 「ルパン三世のタイトルコール」で、タイプライタ音を流しながら、一文字ず つ文字を表示し、最後にタイトルコールのメロディを流す、アニメのタイトル コールそっくりの画面です。 このフォームには、部品を四つ表示します。 メッセージ Box1つ、ボタン1つ、ラベル2つを設定します。 フォームの背景色は「黒」に指定して下さい。 textLabel と、button は、特には設定はしませんが、ボタンには[go]などと表 示し、名前は goButton と名前をつけました。 また、ラベルの「文字色」はどちらも「白」。フォントは「明朝体」にします。 小さい方を label1 として、72 ポイント程度、大きい方を label2 として、288 ポイント程度にします。(はみ出しても、気にしないで下さい。) 11 メッセージ Box の場所は左上、 ボタンの場所はその右側に、適当に配置します。 効果音は、2種類使います。コンストラクタ(資源の確保)と、デストラクタ (資源の解放)を準備します。 これらを含めて、プログラムの先頭部分は以下のようになります。 namespace lupin { public partial class Form1 : Form { System.Media.SoundPlayer typeSound, titleCall; public Form1() { InitializeComponent(); typeSound = new System.Media.SoundPlayer(); typeSound.SoundLocation = "type.wav"; typeSound.Load(); titleCall = new System.Media.SoundPlayer(); titleCall.SoundLocation = "titlecall.wav"; titleCall.Load(); } ~Form1() { typeSound.Dispose(); titleCall.Dispose(); } 音源ファイルをどこに置くかは、実習済みですね? 12 この種類のこれまでの例題と同様に、go ボタンをクリックした時の処理を、以 下のように書きます。ボタンの名前を変えた場合には、イベント処理メソッド の名前を変えたり、貼付ける場所を選ぶなどして下さい。説明済みなのに、き ちんと作れない人が少なくありませんけれど、あえて、これ以上の説明はしま せん。試行錯誤して、うまくできなかったら挙手して下さい。 インデントをきちんと揃える。 どのメソッドの中をプログラムしているのか、意識しながら修正する。 この二つがコツです。 private void goButton_Click(object sender, EventArgs e) { int pos = 0; int x, y; String str; goButton.Visible = false; textBox1.Visible = false; label1.Visible = false; label2.Visible = true; str = textBox1.Text; label1.Text = "XX"; this.WindowState = System.Windows.Forms.FormWindowState.Maximized; this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; while( pos < str.Length ){ label2.Text = str.Substring( pos, 1 ); x = (this.ClientSize.Width - label2.Width)/2; y = (this.ClientSize.Height - label2.Height)/2; label2.Left = x; label2.Top = y; typeSound.Play(); label2.Refresh(); System.Threading.Thread.Sleep(220); pos += 1; } label1.Visible = true; label2.Visible = false; label1.Text = str; titleCall.Play(); x = (this.ClientSize.Width - label1.Width)/2; y = (this.ClientSize.Height - label1.Height)/2; label1.Top = y; label1.Left = x; label1.Refresh(); System.Threading.Thread.Sleep(4500); label1.Visible = false; this.WindowState = System.Windows.Forms.FormWindowState.Normal; this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Sizable; 13 goButton.Visible = true; textBox1.Visible = true; } 今回のプログラムには、一切コメントを入れてありません。 ヒントは、下記の通りです。 Visible → 各部品が見えるか、見えないかの切り替えです。 WindowsState → 全画面表示など、プログラム窓の大きさを設定します。 FormBorderStyle → Window に縁(ふち)があるかないかを設定します。 Refresh → 変更を画面に即時に反映させる際に呼び出されます。 うまく教材にコピー出来ませんが、こんな感じで表示されます。 タイプライタサウンドは、type.wav を用意しました。これは私が適当にタイプ ライタの音に聞こえるように編集したファイルなので、著作権は心配ありませ ん。 あのカッコいい、ルパン三世タイトルコールのメロディも、実はファイルは用 意してありますが、著作権上番組のタイトルコールメロディは、個人で楽しむ 以外はこうした場所に掲載できません。授業中に私の用意したファイルのダウ ンロード URL をお伝えしますが、自分で検索して好きに置き換えてもいいでし ょう。(ここでチャルメラのメロディを流すと、結構笑えます。) 「だから何なの?」と言われかねないアプリですが、好きな文字列を、あのル パン三世のタイトルコールで表示出来るというプログラムです。 14 【今日の発展課題】 「ルパン三世タイトルコール」のプログラムが素材です。 this → 自分自身のフォームを指します。 ClientSize → フォーム中で表示させる領域のサイズ(X, Y)です。 画面上の部品には、全てサイズ(Width と Height)が指定されています。 【発展課題(1)】 X, Y では、センタリングのための座標計算をしていますが、この X と Y の計 算では、なぜこの計算でセンタリングができるか説明してください。(1 点) 【発展課題(2)】 str → textBox1 から入力された文字列が読み出されます。 Substring → str の、pos の場所から1文字を取り出しています。Pos は、 どう初期設定されて、どう変化し、どうループ処理されていますか?(1点) −−ここまでで 10 点満点の配点になります。 【発展課題(3)】 さて、タイトルがやたら長くて(例えば、 「ルパンが主役キャラを下りた後の/ 次元と五右衛門の駆け引き」みたいな・・・)、最後に2行にわたってタイトル を表示するためには、プログラムをどう変更したら良いでしょうか? 自力で着手して完全に動作したら 5 点出します。 仕上がらずに、途中まででも説明があったら評価します。 「自分はこうやろうと思って、ここまでプログラムしたんですが、ここでつま ずきました」的な人は挙手して下さい。デバッグや助言を手伝います。 15