パソコン活用研究シリコンバレー(C、C++、の活用研究)


文字列処理関数

BASICからC言語に移行した時に、最初ちょっとやっかいに感じるのが文字列の処理です。
まず、C言語の文字列処理関数の代表的なものを簡単に説明したあと、C言語の文字列処理
の注意点をBASICと比較しながら説明します。
最後に、これらの文字列処理関数を自作の関数で置き換えてみたプログラムをおまけで
のせておきます。


1 文字列処理関数一覧

【文字列処理関数】
関数 仕様 使用例
strcpy(a, b)

strcpy(a, b)
char a[ ], b[ ]

文字列a に文字列b をコピーする。

(注) '\0' もコピーするので文字列a
はその分も考えて大きさを宣言しておくこと。
char a[10], b[10];

strcpy(a, "some");
strcpy(b, "day");
strcpy(a, b);
strcat(a, b)

strcat(a, b)
char a[ ], b[ ]

文字列a のうしろに文字列b を連結する。

char a[10] = "some";
char b[10] = "day";

strcat(a, b);
strlen(a) strlen(a)
char a[ ]

文字列a の長さを取得。'\0'は長さに含めない。

int len;
char a[] = "today";

len = strlen(a);
printf("長さ = %d\n", len);
strcmp(a, b)

文字列a と文字列b を比較する。

a > b で正、a < b で負、a = bで 0 を返す。

大小関係は辞書順による。

char a[128], b[128];

scanf("%s", a);
scanf("%s", b);

if (strcmp(a, b) == 0) {
	printf("等しい文字列\n");
} else {
	printf("異なる文字列\n");
}
strncpy(a, b, n) 文字列a に文字列b の先頭 n文字をコピーする。
strncat(a, b, n) 文字列a のうしろに文字列b の先頭 n文字を連結する。
strncmp(a, b, n) 先頭 n文字だけ文字列a と文字列b を比較する。


文字列処理関数を使った簡単なサンプルプログラムを以下にあげておきますので、各関数の使い方
を確認してみて下さい。

#include "string.h"
#include "stdio.h"

int main(int argc, char* argv[])
{   int len;
    char a[10]="abc",b[10]="xyz",c[10];
 
 strcpy (c,a);
 strcat (c,b);
 len=strlen(c);
 printf("文字列a:%s,  文字列b:%s,  文字列c:%s,  文字列c の長さ%d\n",a,b,c,len);

 if (strcmp(a, b) == 0) {
        printf("%s, %s は等しい文字列\n",a,b);
} else {
        printf("%s, %sは異なる文字列\n",a,b);
}
 if (strcmp(a, "abc") == 0) {
        printf("%s, %s は等しい文字列\n",a,"abc");
} else {
        printf("%s, %sは異なる文字列\n",a,"abc");
}
        return 0;
}

実行例

D:\win95\C>string
文字列a:abc, 文字列b:xyz, 文字列c:abcxyz, 文字列c の長さ6
abc, xyzは異なる文字列
abc, abc は等しい文字列


2 文字列処理の注意点
文字列の処理にあたっての注意点を、BASICとの比較でみてみましょう。
BASICプログラマには要注意な点が多々ありますので、しっかり理解しておいて下さい。

C言語では、文字列型という変数の型がなく、文字型Charで配列を使うか、ポインタ変数を使う
ことにより文字列を表現するので、文字列の処理はいささか厄介です。
BASICでは文字列の代入や連結などは以下のように簡単でした。
A$="abcd"
B$="xyz"
C$=A$
D$=B$+C$
(非常に簡単ですね)

C言語ではどうでしょうか。以下の例を見てください。、
char *a,*b,*c,*d;
a = "abcd"; .........(1)
b = "xyz"; ...........(2)
c = a; ....................(3)
d = b + c; ...........(4)

(1),(2)はO.K.ですが、(3) はポインタの代入なので、BASICのC$ = A$の意味とは異なります。
ポインタ変数cにはポインタ変数aのポインタ(アドレス)が入るので、結果としてcは"abcd"をさしますが、
BASICと同じ意味での代入(すなわち、値渡し=値のコピー)なら strcpy(c,a); とすべきです。
ここはやっかいで、一見、正しい結果がでたように見えるので、BASICからC言語に移行した
ばかりの時にやってしまいがちなミスです。文字列を扱うプログラムでどうも意図したとおりにならない
という時は、うっかりBASIC流にこんなミスをしていることが多いと思います。
(もちろん、意図的にポインタの代入をしているのであれば、c = a; で正しい記述です)

ここはBASICと根本的に違うところで、うっかりBASICの頭のままにプログラムするととんでもないミスに
なる点なので、しっかり理解して下さい。。何のことか意味不明?という方は以下をよく読んで下さい。
C言語では、c = a; とすると、cとaは同じポインタをさすことになります。従ってaを a = "123"と変更すると
cも"123"になってしまいます。
BASICだったら
a$ = "abcd"
c$ = a$
a$ = "123"
とした場合、a$は"123"に変更されますが、c$は"abcd"のままですよね。

(4)は論外です。とんでもない結果になります。ポインタ同士の足し算なので、BASICのD$=B$+C$
とはまったく異なります。BASICのD$=B$+C$の意味なら、これは
strcpy(d, b);
strcat(d, c);
としなければなりません。

c = a; とstrcpy(c, a); の違いを実際にプログラムで見てみましょう。

#include <stdio.h>
#include <string.h>

main(){
char *a,*b,*c;
a="abcd";
strcpy(b,a);
c=a;
printf("%d, %s, %d, %s, %d, %s",a,a,b,b,c,c);
}

実行結果です

D:\win95\C>string3
102, abcd, 330, abcd, 102, abcd

aもbもcも結果として"abcd"をさしていますが、ポインタ(アドレス)は、aとcは同じ102
bは330です。
これは、aの値("abcd")を
値渡し (すなわち値をコピー) しているのか、アドレス(ポインタ)参照渡しなのかという
違いと同じです。
値渡し、参照渡しについては、変数の有効範囲とポインタ、関数のデータ受け渡しで詳しく
解説してますので、参照して下さい。


3 おまけ
関数自体に慣れてしまえば、C言語の文字列処理もそうとまどうことはないでしょう。
ま、ここで今回は終わりにしてもいいのですが、ちょっとそっけない感じもしますので、
今回説明した文字列処理関数を、自作の関数に置き換えたプログラムを作ってみました。
strcpy --> stringcopy
strcat --> stringcat
strlen --> stringlength
strcmp --> stringcmp
という自作の関数で置き換えてみました。

文字列の処理をする場合、文字列が終端('\0')かどうかを判定する、というのがキーポイントに
なります。

#include "string.h"
#include "stdio.h"
void stringcopy(char *, char *);
void stringcat(char *, char *);
int stringlength(char *);
int stringcmp(char *, char *);

int main(int argc, char* argv[])
{    int len;
    char a[10]="abc" ,b[10]="xyz",c[10];
   stringcopy(c,a);
  stringcat(c,b);
 len=  stringlength(c);
  printf("文字列a: %s, 文字列b:%s, 文字列c:%s\n",a,b,c);
  printf("文字列c の長さ %d\n",len);
 if (stringcmp(a, b) == 0) {
	printf("%s, %s は等しい文字列\n",a,b);
} else {
	printf("%s, %s は異なる文字列\n",a,b);
}
 if (stringcmp(a, "abc") == 0) {
	printf("%s, %s は等しい文字列\n",a,"abc");
} else {
	printf("%s, %s は異なる文字列\n",a,"abc");
}
}

void stringcopy(char *x, char *y)
{
while (*y != '\0')
{  
*x=*y;
x++; y++;
}
*x='\0';
}

int stringlength(char *x)
{ int i;
i=0;
while (*x != '\0')
{i++;
 x++;
}
return i;
}

void stringcat(char *x, char *y)
{ int lenx,leny,i;
lenx = stringlength(x);
leny =stringlength(y);

for (i=lenx; i<=lenx+leny; i++)
{x[i] = y[i-lenx];}
} 

int stringcmp(char *x, char *y)
{
while(*x != '\0')
{
if (*y == '\0') {return 1;}
if (*x > *y) {return 1;}
if (*x < *y) {return -1;}
x++;y++;
}
if (*y == '\0'){return 0;}
return -1;
}

実行結果

D:\win95\C>string2
文字列a: abc, 文字列b:xyz, 文字列c:abcxyz
文字列cの長さ 6
abc, xyzは異なる文字列
abc, abc は等しい文字列

上のプログラムが理解できれば、文字列処理関数の理解も十分な域にあると思います。


TopPage