プログラミング及び実習⑧
文字列処理の標準ライブラリ関数(1/2)
これらの関数を使うためには、ヘッダファイル<string.h>をインクルードしておく必要がある。
(変数s:char *,変数t:const char *,変数n: int)
関数 char *strcpy(s, t) char *strncpy(s, t, n) char *strcat(s, t) char *strncat(s, t, n) 機能 文字列tをsにコピーし、sを返す。 文字列tの最初のn文字をsにコピーし、 sを返す。tがn文字より少ないとき、 strcpy()と等価になる。 文字列tを文字列sの終わりに連結し、 sを返す。 文字列tの最初のn文字をsの終わりに 連結し、sを返す。tがn文字より少ない とき、strcat()と等価になる。
文字列処理の標準ライブラリ関数(2/2)
関数 機能 int strcmp(s, t) 文字列sとtを比較。s==tなら0を、 s<tなら負の値を、s>tなら正の値を返す。 ここでの大小は辞書順である。 int strncmp(s, t, n) 文字列sとtの最初のn文字を比較。 s==tなら0を、s<tなら負の値を、 s>tなら正の値を返す。 ここでの大小は辞書順である。 int strlen(s) sの長さ(文字数)を返す。strncpy(s, t, n)について
• strncpy(s, t, n)の動作は少し複雑である。nの値と文字列tの 長さ(文字数)の関係により、tのn文字分をsにコピーした後 にヌル文字’¥0’が自動付与されたりされなかったりする。 • すると、sを初期化しておかないと、危険である。 • したがって、「最初のn文字分をコピーする」という機能だけ を利用したいとき、以下のようにプログラムを書くと安全。 strncpy(s, t, n); m=strlen(t); if(m<n) s[m]=‘¥0’; else s[n]=‘¥0’; //つまり、末尾に強制的にヌル文字を付与する。strncpy(s, t, n)の詳細
• 文字列変数
s
に文字列変数
t
の値を先頭か
ら
n
文字コピーする。ただし、
①
t
の長さ(文字数)が
n
以上のときには
n
文字をコピーする。しかし、
‘¥0’は付け加え
ない。
②
t
の長さが
n
より少ない場合には残りの
文字を
‘¥0’
で埋める。
したがって、①の場合、sの初期化なしでは
この関数は使えない。
参考strncpy(s, t, n)の使用例
#include <stdio.h> #include <string.h> int main(void){ char s[16]="abcdefg", t[]="1234"; strncpy(s, t, 4); // ①の場合:つまり、strlen(t)>=n puts(s); strncpy(s, t, 10);//②の場合:つまり、strlen(t)<n puts(s); return 0; } 実行結果: 1234efg → s: 1234efg¥0 1234 → s: 1234¥0¥0¥0¥0¥0¥0 参考 元のsの後半 tの値がコピー される7
1.以下の関数呼び出しの値(返値)を求めよ
1)strcmp(“ghi”, “ja”); /*
:=0, <0, >0
*/
2)strncmp(“gk”, “gkh”, 2); /*
:=0, <0, >0
*/
3)strlen(“xyz”);
2.以下の関数を呼び出した場合のsの値を求めよ
1)strcpy(s, “Ryukoku Uni.”);
2)strncpy(s, “Ryukoku Uni.”, 7);
s[7]=‘¥0’;
3)strcpy(s, “Ryukoku ”);
strcat(s, “Uni.”);
4) strcpy(s, “Ryukoku ”);
strncat(s, “University”, 3);
演習問題1
空白1つ演習問題2
以下のプログラムを作成しなさい。
(1) 文字列変数(文字の一次元配列)
s1
を、
文字列
Ryukoku Uni.
で初期化して宣言し、
文字列変数
s2
を初期化なしで宣言する。
(2)
s1
の値を
s2
にコピーし、
s2
の値を出力
する。
関数strcpy(s, t)ーその①
void strcpy(char s[], char t[]) { int i=0; while(t[i] != '¥0'){ s[i]=t[i]; i++; } s[i]='¥0'; }
void strcpy(char s[], char t[]) { int i=0; while((s[i]=t[i]) != '¥0') i++; } = s[i]='¥0'; なぜ不要?
関数strcpy(s, t)ーその②
void strcpy(char *s, char *t) { int i=0; while(t[i] != '¥0'){ s[i]=t[i]; i++; } s[i]='¥0'; } このようにポインタ変数で宣言する場合、 文字列変数をchar *型とも呼ばれる。
関数strcpy(s, t)ーその③
void strcpy(char *s, char *t) { while(*t != '¥0'){ *s=*t; s++; t++; } *s='¥0'; }
復習:ポインタ
*t or t[0] t メモリ R t+1 y *(t+1) or t[1]void strcpy(char *s, char *t) { while(*t != '¥0'){ *s=*t; s++; t++; } *s='¥0'; } int main(void) { char t[]=“Ryukoku”, s[64]; *s=*t s R s+1 y *(s+1)=*(t+1) s[0]=t[0] s[1]=t[1]
関数strcpy(s, t)ーその④
(標準関数に近い!)
char *strcpy(char *s, char *t) { int i=0; while((*(s+i)=*(t+i)) != '¥0') i++; return s; } =
char *strcpy(char *s, char *t) { int i=0; while((s[i]=t[i]) != '¥0') i++; return s; }
標準関数strcpy()の戻り値の利用
include <stdio.h>
include <string.h>
int main(void)
{
char s1[]="Ryukoku Uni.", s2[64],
*s3
;
s3
=strcpy(s2, s1);
puts(s2);
puts(s3);
puts(
strcpy(s2,s1)
);
return 0;
演習問題3
例1:変数名チェック
• 文字列データを入力し、それがC言語の変数名と
言えるかどうかを調べる
使用できる文字: (大小)英文字、数字、アンダースコア(_) 。 ただし、最初の文字は数字であってはならない。 (識別される文字数: 最初から8文字目まで) 出力例: 変数名=asd asd 変数名=_12 _12 変数名=a 2 a 2は変数名として適切でない 変数名=a12345678 空白17
プログラム(1/2)
#include <stdio.h> #include <ctype.h> #include <string.h> #define N 64 int main(void) { int i, flag; char s[N]; while(1){ flag=0; printf("変数名=");if(fgets(s, N, stdin) == NULL) break;
s[strlen(s)-1]=‘¥0’; // 改行記号を取り除く
if(strlen(s)>8) flag=1;
else if(!isalpha(s[0]) && s[0] != '_') flag=1;
改行記号があると、次のページの 出力(printfの方)が少し困る。
プログラム(2/2)
else{
for(i=1; s[i] != '¥0'; i++){
if(!isalnum(s[i]) && s[i] != '_'){ flag=1; break; } } } if(flag==0) puts(s); else printf("%sは変数名として適切でない¥n", s); } // while(1) return 0; }
補足説明
fgets()で読み込んだ
s
は以下のようなもの
s[0] s[1] s[2] s[3] s[4] s[5] s[6] s[7] s[8] s[9] s[10] s[11] s[12] R y u k o k u U n i ¥n ¥0s[strlen(s)-1]=‘¥0’;
で
s
は以下になる
s[0] s[1] s[2] s[3] s[4] s[5] s[6] s[7] s[8] s[9] s[10] s[11] s[12] R y u k o k u U n i ¥0 ¥0つまり、sの中から改行記号¥nを取り除いた。
なお、たとえば
strcmp(s, “abc”)
が正しく機能できるのは、
s
に改行記号がないもの。
例2:ファイルデータの整形
• 以下のipt.datファイルをopt.datファイルに
変換するプログラム。但し、ipt.datの中の
空白は空白かタブであり、opt.datの中の名
前と点数の間は1個のタブである。
データ
opt.dat
Student
Score
Tanaka 79
Nakata 73
Hashimoto 89
Nakahashi 67
Nakamoto 99
ipt.dat
Student
Score
Tanaka 79
Nakata
73
Hashimoto 89
Nakahashi
67
Nakamoto 99
プログラム
#include <stdio.h> #define N 64 int main(void) { char s1[N], s2[N], line[N];while(fgets(line, N, stdin) != NULL){ if(line[0]==‘¥n’) printf(“¥n”); else{ sscanf(line, “%s %s”, s1, s2); printf("%s¥t%s¥n", s1, s2);} } return 0;
23
例3:データの更新
さらに特定の人名を修正する場合 #include <stdio.h> #include <string.h> #define N 64 int main(void) { char s1[N], s2[N], line[N];while(fgets(line, N, stdin)) != NULL){ if(line[0]==‘¥n’) printf(“¥n”);
else{
sscanf(line, "%s %s", s1, s2);
if(strcmp(s1, “Nakamoto”)==0) strcpy(s1, “Naka”);
printf("%s¥t%s¥n", s1, s2);} }
return 0; }
演習問題4
以下の処理を実行すると、どのような出力
が得られるか。
(1) int a[]={1,2,3,4};
for(sum=0,i=0; i<4; i++)
sum += a[i];
(2) char a[]=“1234”;
for(sum=0,i=0; i<4; i++)
sum += a[i]-’0’;
25
演習問題5
以下のようなプログラムを作成しなさい。 (1)文字列変数a,b,cを用意する。 (2)文字列変数aとbにキーボードから任意の文字列を入力 する。 (3)文字列変数cにaの内容をコピーする。 (4)cの中身を出力する (5)cの内容にbの内容を連結する。 (6)cの中身を出力する。 (7)上記(2)~(6)の処理を文字列QUITが入力されるまで 繰り返す。 ヒント:「任意の文字列(本問題ではQUIT)でループを抜け 出す」ような機能を実現するために、いつもの while(fgets...)をやめてwhile(1)とかにしてループの中で 文字列比較の関数を使うとよいでしょう。演習問題5の実行例
./a.out
Input a:
This is a test 入力
Input b:
:OK!
入力
c: This is a test
c: This is a test:OK!
Input a:
Ryukoku 入力
Input b:
University
入力
c:
Ryukoku
c:
Ryukoku University
Input a:
QUIT
入力
終了
第8回実習課題(1/5)
1.標準関数strncpy()と類似した機能を持つ関数
void StrnCpy(char *s, char *t, int n)
を作成し、
main()からそれを呼び出すようなプログラムを作
成してその動作を確認しなさい(
ex08-strncpy.c
)。
ただし、
*t
に格納されている文字列の長さを
m
と
すれば、
m<n
の場合に
*s
の(0から数えての)
m
文
字目に、
m>=n
の場合に
*s
の
n
文字目にヌル文字
(¥0)を与える。なお、
m
を求めるのに関数
strlen()
を使わないこと。
28