Lesson 8
文字列
目次
Lesson 8
Chapter 1
文字列
C言語の文字列は、文字の配列、つまりchar型などの配列として表現されています。配列の要素は文字(文字コード)で、通常の配列と同様に、添字を使って各要素(文字)を参照したり、値(文字)を格納したりできます。ここでは文字列の仕組み、文字配列の宣言と初期化、文字列の入力について学びます。
文字列リテラル、ナル文字、文字列、空文字列
文字列リテラル
プログラムに文字列を書くには、次のように"(ダブルクォーテーション)で囲んで書きます。これは文字列リテラルと呼ばれます。
文字列リテラル
"文字列"
文字列リテラルを並べて書くと、コンパイル時に1個の文字列へ連結されます。これは長い文字列を書くときや、複数行にわたって文字列を書くときなどに便利です。文字列リテラルと文字列リテラルの間には「空白」「タブ」「改行」を入れることができます。
文字列リテラル連結
"文字列a" "文字列b"
puts関数を使って3個の文字列リテラルに分けて記述し、出力するプログラムを書いてみましょう。
string1.c
#include <stdio.h>
int main(void)
puts("START\n"
"EXECUTE\n"
"END");
return 0;
}
実行結果
string1.exe
START
EXECUTE
END
ナル文字
ナル文字とは、特殊な文字であり「文字がない、空である」ということを示す文字のことです。
ナル文字(ヌル文字)
ナル文字のことを「ヌル文字」と呼ぶ流儀もあります。C言語にはヌル文字の他に、ヌルポインタという機能もあります。どちらもヌルですが、ヌル文字とヌルポインタは用途が異なるので、混同しないように注意してください。単に「ヌル」と呼ぶと紛らわしいので「ナル文字」、「ヌル文字」、「ヌルポインタ」のように呼ぶとよいでしょう。
文字列
C言語では、文字列はchar型に配列に格納して扱うことができます。以下のように宣言します。
文字配列の宣言
char 文字配列名[要素数];
空文字列
空文字列は「""」と表現します。これは1つの文字として認識されます。
文字列と配列
先ほども述べましたが、C言語で文字列を型に格納する際には「char型の配列」を使用します。例えば"ABCDE"という文字列を配列に格納するためのコードは以下のようになります。
文字配列
char str[6]
str[0] = 'A';
str[1] = 'B';
str[2] = 'C';
str[3] = 'D';
str[4] = 'E';
str[5] = '\0';
文字配列の初期化の場合は、次のように記述することも可能です。
文字配列の初期化①
char str[6] = "ABCDE";
文字配列の初期化②
char str[] = "ABCDE";
scanfを使った文字列の読み込み
文字列を入力する方法を紹介します。scanf関数を使った方法で、キーボードから入力した文字列を配列に格納し、末尾にナル文字を付けます。以下のように、変換指定子は「%s」を使います。
string2.c
#include <stdio.h>
int main(void)
char str[100];
printf("input string:");
scanf("%s", str);
printf("%s\n", str);
return 0;
}
実行結果
string2.exe
input string:1234567890
1234567890
上記の文字配列名には、&(アドレス演算子)がついていません。他の配列と同様に、文字配列名は配列の先頭アドレスを表すので、&でアドレスを取得する必要がありません。ポインタ変数に配列のアドレスを格納することも可能です。アドレスを表示して確認してみましょう。
string2.c
#include <stdio.h>
int main(void)
printf("input string:");
char str1[100];
// 配列str1の「先頭のアドレス」をポインタ変数に格納
char *str2 = str1;
scanf("%s", str2);
printf("%s\n", str2);
puts("");
printf("%p\n", str1);
printf("%p\n", str2);
return 0;
}
実行結果
string2.exe
input string:1234567890
1234567890
000000000061FDB0
000000000061FDB0
コンパイラによっては要素数以上の文字が入力できてしまう
「char str1[100];」と100文字まで入力できる文字配列を宣言していますが、コンパイラによっては要素数以上の文字列を格納できます。正常に動作するとは限らないので、コンパイラではなく、自分で書いたコードを基準に考えた方がよいでしょう。
ナル文字を利用した繰り返し処理
文字列の長さ(文字数)を調べてみましょう。簡単にするために、ここでは末尾のナル文字を除いた要素数を数えることにします。標準ライブラリのstrlen関数を使うと、文字列の長さを簡単に調べられます。「str」はstring (文字列)、「len」はlength (長さ)の略と考えられます。strlen関数は次のように使います。文字列には、文字列リテラルや文字配列(配列名)を指定します。strlen関数は文字列の長さを、size_t(サイズティー)型の値として返します。size_tは符号なしの整数型で、実際の型は処理系によって異なります。
string2.exe
strlen(文字列)
文字列の長さを調べるプログラムを自作する場合には、例えば次のような方法で長さを調べます。文字列の最初の要素(文字)から始めて、ナル文字でない限り、次の要素に進むことを繰り返します。何回進んだのかをカウントしておけば、これが長さになります。
string3.c
#include <stdio.h>
#include <string.h>
int main(void)
char str[] = "C language";
// strlen関数で文字列の長さを取得
printf("&d\n", strlen(str));
// 自作関数で文字列の長さを取得
int i;
for(i = 0; str[i]; i++);
printf("%d\n", i);
return 0;
}
実行結果
string3.exe
10
10
実行結果は10文字でした。strlen関数の結果と一致したので、自作のプログラムは正しく動いていると思われます。上記のプログラムでは、変数iが文字列の長さを表します。iを0で初期化しておき、ヌル文字が出現する(str[i]が0になる)まで、i++で回数を数えます。
大文字と小文字の変換
「ctype.h」をincludeファイルに追加することで、大文字・小文字変換する関数を使用することができます。
toupper関数(小文字を大文字に変換)
int toupper(int c);
tolower関数(大文字を小文字に変換)
int tolower(int c);
今回は標準ライブラリ関数を使用せずに、英大文字から英小文字、英小文字から英大文字に変換する関数を作成します。英小文字や英大文字は「アスキーコード」と呼ばれる対応表で一意に数値が決まっています。英小文字と英大文字の部分のみを下記に抜粋します。
16進数 | 英大文字 | 16進数 | 英小文字 |
0x41 | A | 0x61 | a |
0x42 | B | 0x62 | b |
0x43 | C | 0x63 | c |
0x44 | D | 0x64 | d |
0x45 | E | 0x65 | e |
0x46 | F | 0x66 | f |
0x47 | G | 0x67 | g |
0x48 | H | 0x68 | h |
0x49 | I | 0x69 | i |
0x4A | J | 0x6A | j |
0x4B | K | 0x6B | k |
0x4C | L | 0x6C | l |
0x4D | M | 0x6D | m |
0x4E | N | 0x6E | n |
0x4F | O | 0x6F | o |
0x50 | P | 0x70 | p |
0x51 | Q | 0x71 | q |
0x52 | R | 0x72 | r |
0x53 | S | 0x73 | s |
0x54 | T | 0x74 | t |
0x55 | U | 0x75 | u |
0x56 | V | 0x76 | v |
0x57 | W | 0x77 | w |
0x58 | X | 0x78 | x |
0x59 | Y | 0x79 | y |
0x5A | Z | 0x7A | z |
このアスキーコードをよく見てください。規則性があるのがわかります。
・A、B、C…の順に16進数の数が1ずつ増えている
・Aとa、Bとb、Cとc…の差は20
値は16進数ですが、数字であることには変わりません。Lesson6で基数について学習しましたが、16進数を書くときは先頭に「0x」の「整数定数」をつけます。A~Zまでの数の範囲は「0x41」~「0x5A」、a~zまでの数の範囲は「0x61」~「0x7A」、あとは規則性に従って、演算を行えば値の変換は可能です。プログラムのイメージができたら、コードを書いていきましょう。main関数のほかに、toUpper関数(英子文字 → 英大文字)とtoLower関数(英大文字 → 英子文字)を作成します。char型の'a'を'A'に変換するためにはどういった計算が必要になるか、関数を作成するためには引数はどうするか、処理を細分化して考えるとイメージしやすいと思います。
string4.c
#include <stdio.h>
int toUpper(int character) {
// 英小文字の場合は英大文字へ変換
if ((character >= 'a') && (character <= 'z')) {
// 20を減算
return c - 0x20;
}
// 英小文字以外の場合
return character;
}
int toLower(int character) {
// 英大文字の場合は英小文字へ変換
if ((character >= 'A') && (character <= 'Z')) {
// 20を加算
return character + 0x20;
}
// 英大文字以外の場合
return character;
}
int main(void)
{
printf("toUpper\n");
printf("a => %c\n", toUpper('a'));
printf("b => %c\n", toUpper('b'));
printf("c => %c\n", toUpper('c'));
printf("d => %c\n", toUpper('d'));
printf("e => %c\n", toUpper('e'));
printf("toLower\n");
printf("A => %c\n", toLower('A'));
printf("B => %c\n", toLower('B'));
printf("C => %c\n", toLower('C'));
printf("D => %c\n", toLower('D'));
printf("E => %c\n", toLower('E'));
return 0;
}
実行結果
string4.exe
a => A
b => B
c => C
d => D
e => E
A => a
B => b
C => c
D => d
E => e
文字の範囲をチェックして、変換対象の文字かどうかをif文で判定します。その後、英大文字であれば「- 0x20」、英子文字であれば「+ 0x20」という計算を行って対象文字を変換します。完成したコードを確認すると、コード自体はシンプルで難しい処理は行っていないことが分かります。
