アルゴリズムと データ構造

アルゴリズムと
データ構造
第12回
文字列処理
C言語の文字列

文字列とは
 プログラム上で文字の並びを表す


中身が空でもよい
文字列リテラル
 文字の並びを2つの引用符で囲んだもの
 各文字は記憶域上で連続して配置
 終端にはナル文字が自動的に付加
 内容は基本的に書き換え不可
C言語の文字列

文字列リテラル
文字列リテラル“STRING”
01010011
文字’S’ (0x53)
S T R I N G \0
01010100
文字’T’ (0x54)
01010010
文字’R’ (0x52)
01001001
文字’I’ (0x49)
01001110
文字’N’ (0x5E)
01000111
文字’G’ (0x47)
00000000
ナル文字’\0’ (0)
C言語の文字列

文字列リテラルの特徴
 型と値

char *型・評価値は先頭文字へのポインタ
 記憶域期間

静的記憶域期間:プログラム開始から終了まで記憶域を占有
 同一つづりの文字列リテラル

格納領域が同一か別かは処理系依存
 定数性

文字列リテラルの記憶域への値の書き込みは定義なし:どうなる
かは処理系依存・やるべきではない
C言語の文字列

配列による文字列
 文字列をchar型配列に格納することで、値の読
み書きが可能
 終端にはナル文字が必要
ナル文字がないとプログラム暴走の恐れ
 ナル文字以降どのような内容があろうと無視

配列
S T R I N G \0
文字列
無視
C言語の文字列

文字列の初期化
 配列宣言の際に初期化可能
 代入式では初期化式無効
char st[10] = {‘S’, ‘T’, ‘R’, ‘I’, ‘N’, ‘G’, ‘\0’};
char st[10] = “STRING”;
/* 上の式と同等 */
char st[10];
st = {‘S’, ‘T’, ‘R’, ‘I’, ‘N’, ‘G’, ‘\0’};
st = “STRING”;
/* エラー */
/* エラー */
C言語の文字列

ポインタによる文字列
 文字列リテラルを指し示すのにポインタを使用
 各文字へのアクセスは配列へのアクセスと同様
pt
char *pt = “1234”;
ポインタ
1 2 3 4 \0
文字列リテラル
・仮に文字列リテラルが1000番地から格納されていれば、ptには1000が入る
文字列の基本操作

文字列の長さ
 配列要素数と文字列長は必ずしも一致しない

配列全体を使い切っているとは限らない
 文字列先頭からナル文字を線形探索
最初に見つかったナル文字までが文字列
 ナル文字のある要素の添え字が文字列の長さ



ナル文字はカウントしない
必ずナル文字が存在:探索失敗を考慮する必要なし

ある意味番兵法ともいえる
文字列の基本操作

文字列の長さ
int str_len(const char *s) {
int len = 0;
while (s[len])
len++;
return (len);
}
文字列の基本操作

文字列の長さ
int str_len(const char *s) {
int len = 0;
while (*s++)
len++;
return (len);
}
文字列の基本操作

文字列の長さ
int str_len(const char *s) {
char *p = s;
while (*s)
s++;
return (s - p);
}
文字列の基本操作

文字列からの文字の探索
 文字列先頭から対象文字を線形探索
最初に見つかった位置が求める位置
 見つかる前にナル文字がきたら探索失敗

文字列の基本操作

文字列からの文字の探索
int str_chr(const char *s, int c) {
int i;
for (i = 0; s[i] != c; i++)
if (s[i] == ‘\0’)
return (-1);
return (i);
}
文字列の基本操作

文字列からの文字の探索
char *str_chr(const char *s, int c) {
for ( ; *s != c; s++)
if (*s == ‘\0’)
return (NULL);
return ((char *) s);
}
文字列の基本操作

文字列の比較
 2つの文字列を先頭から順に比較
すべての文字が等しかったら等しいと判断
 途中で違う文字列がきたら大小比較

違う部分の文字のそれぞれの文字コードの大小で比較
 文字コードの体系により比較結果に差異

文字列の基本操作

文字列の比較
int str_cmp(const char *s1, const char *s2) {
while (*s1 == *s2) {
if (*s1 == ‘\0’)
return (0);
s1++; s2++;
}
return ((unsigned char) *s1 - (unsigned char) *s2));
}
文字列探索

文字列探索とは
 文字列中に、別の文字列が存在するかを探索
探し出す文字列:パターン
 探索される文字列:テキスト

文字列探索

力まかせ法
 単純法・素朴法
 テキストの先頭から順次パターンと比較
 一致するまで、パターン位置を1文字ずつずらし
て再比較

最後まで一致しなかったら含まれない
文字列探索

力まかせ法
A
B
A
B
A
B
C
\0
C
D
\0
E
F
G
H
A
\0
文字列探索

力まかせ法
pt
A
B
A
B
A
B
C
\0
pp
C
D
\0
E
F
G
H
A
\0
文字列探索

KMP法
 Knuth-Morris-Pratt法
 不一致文字に出会ったときのそれまでの照合結
果を有効利用
 テキスト中とパターン中の重なり部分を見つけ、
パターンの移動を大きく
 テキスト走査位置が前進のみ
文字列探索

KMP法
Z
A
B
C
A
B
X
A
B
C
B
C
A
A
B
D
B
\0
D
A
C
C
A
D
E
F
\0
文字列探索

KMP法における照合再開位置
A
B
C
A
B
D
\0
A
B
C
A
B
D
\0
A
B
C
A
B
D
0
0
0
1
2
0
文字列探索

KMP法
pt
Z
A
B
C
A
B
X
A
C
C
A
B
C
B
C
A
A
B
D
B
\0
D
A
B
C
A
B
D
0
0
0
1
2
0
pp
A
D
E
F
\0
文字列探索

Boyer-Moore法
 BM法
 照合をパターン末尾から実行
 一致しない文字がきたとき、その文字に応じてパ
ターンの移動量を決定
文字列探索

Boyer-Moore法
A
B
C
X
D
A
B
A
C
\0
E
Z
C
A
B
A
A
C
A
B
A
C
\0
文字列探索

Boyer-Moore法におけるスキップテーブル
 パターンと一致しなくなった文字ごとに、照合中の
文字を何文字分ずらすかの移動量を計算
 パターン文字長がnの場合
パターンに含まれない文字の移動量:n
 パターンに含まれる文字の移動量:n - k - 1

k:最後に出現する位置の添え字
 パターン末尾の文字はノーカウント

文字列探索

Boyer-Moore法におけるスキップテーブル
0
1
2
3
A
B
A
C
A
1
B
2
C
4
D
4
E
4
F
4
G
4
H
4
I
4
J
4
K
4
L
4
M
4
N
4
O
4
P
4
Q
4
R
4
S
4
T
4
U
4
V
4
W
4
X
4
Y
4
Z
4
文字列探索

Boyer-Moore法
pt
A
B
C
X
D
A
B
A
C
\0
E
Z
C
A
B
pp
A
1
B
2
AB以外
4
A
C
A
B
A
C
\0