引数を取って文字列を表示するプログラムの修正

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
slei

引数を取って文字列を表示するプログラムの修正

#1

投稿記事 by slei » 14年前

以前、ここで一度質問させてもらったものです。
またプログラムで躓いてしまい、どうにも詰まってしまったので質問をさせてもらいにきました。
示されたプログラムを、「コマンド引数が与えられていない場合にはHello,everyone.を返し、引数が1個の場合や空白を含めた
文字数が99字を超える場合も正しく動作するように修正する」という課題が出たのですが、引数による管理ということで
手法はある程度浮かんだのですが、プログラム自体の動作がつかみきれておらず、どこにどう手を加えればいいのかがこんがらがっています。
OSはLinuxです。どうかよろしくお願いします。

コード:

#include <stdio.h>
#define LEN 100

int main(int argc, char *argv[]){
  int i,j;
  int index;
  static char name[LEN];

  /* */
  index=0;

/* 自己記述 */
  if(argc == 1) /* 引数がない場合nameにeveryoneを入れる */
    name[] = "everyone"; /* hello_to.c:14: error: expected expression before ']' token のコンパイルエラー*/
/* */

  else{
    for(i=1; i<3; i++){ /* ループ回数を引数の数で管理 >argc? */
    for(j=0; argv[i][j] != '\0'; j++){
      name[index++]=argv[i][j];
    }
    if (i==2){ /* この部分の処理がよくわかっていない */
      name[index] = '\0';
    } else {
      name[index++]=' ';
    }
   }
  }

  /* 文字数が100を越えた場合も動作するような処理を入れる */

  printf("Hello, %s.\n",name);
  return 0;
}

アバター
bitter_fox
記事: 607
登録日時: 14年前
住所: 大阪府

Re: 引数を取って文字列を表示するプログラムの修正

#2

投稿記事 by bitter_fox » 14年前

slei さんが書きました: 示されたプログラムを、「コマンド引数が与えられていない場合にはHello,everyone.を返し、引数が1個の場合や空白を含めた
文字数が99字を超える場合も正しく動作するように修正する」という課題が出たのですが、引数による管理ということで
いくつか仕様を確認させてください。
次の入力の時はどういった出力がされればよいのでしょうか?
1, >>program.exe A
2, >>program.exe A B
3, >>program.exe A B C D E
4, >>program.exe "A B C D" E

また、「空白を含めた文字数」とありますがこの文字数とは何の文字数なのでしょうか?(引数一つ当たりの文字数、引数全体の文字数、etc...)
slei さんが書きました:

コード:

  static char name[LEN];

    name[] = "everyone"; /* hello_to.c:14: error: expected expression before ']' token のコンパイルエラー*/
それから、char型配列への文字列の代入はstrcpyを使用してください。
http://www9.plala.or.jp/sgwr-t/lib/strcpy.html
(name[] = "everyone"と言ったようなことは許可されていません。
nameがchar型ポインタであればname = "everyone"は可能です。)

アバター
ゆーずぃ
記事: 62
登録日時: 14年前
住所: 埼玉県

Re: 引数を取って文字列を表示するプログラムの修正

#3

投稿記事 by ゆーずぃ » 14年前

bitter_foxさんの補足をさせて頂くと。


a = "abc";
の形式で代入が出来るのは宣言の時だけです。つまり
可  char a[] = "abc";
不可 a = "abc";

ですので、今回の場合はポインタを使うか一文字ずつ代入するかstrcpyなどの関数を使う必要があるということです。
最後に編集したユーザー ゆーずぃ on 2011年5月11日(水) 17:57 [ 編集 1 回目 ]

アバター
bitter_fox
記事: 607
登録日時: 14年前
住所: 大阪府

Re: 引数を取って文字列を表示するプログラムの修正

#4

投稿記事 by bitter_fox » 14年前

bitter_fox さんが書きました: 次の入力の時はどういった出力がされればよいのでしょうか?
1, >>program.exe A
2, >>program.exe A B
3, >>program.exe A B C D E
4, >>program.exe "A B C D" E
追加で次もお願いします。
5, >>program.exe AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA B
6, >>program.exe AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB

5は第一引数が99字えているものとします。
6は第一引数及び第二引数の合計が99字を超えているものとします。

それから変数nameは絶対に使用しなければならないのでしょうか?(引数に与えられた名前を格納する変数)
ゆーずぃ さんが書きました:bitter_foxさんの補足をさせて頂くと。
a = "abc";
の形式で代入が出来るのは宣言の時だけです。
ですので、今回の場合はポインタを使うか一文字ずつ代入するかstrcpyなどの関数を使う必要があるということです。
補足ありがとうございます。

[hr][削除]
操作ミスにより重複投稿してしまったので片方を削除しました。

slei

Re: 引数を取って文字列を表示するプログラムの修正

#5

投稿記事 by slei » 14年前

返信ありがとうございます。
bitter_fox さんが書きました:
bitter_fox さんが書きました: 次の入力の時はどういった出力がされればよいのでしょうか?
1, >>program.exe A
2, >>program.exe A B
3, >>program.exe A B C D E
4, >>program.exe "A B C D" E
追加で次もお願いします。
5, >>program.exe AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA B
6, >>program.exe AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB

5は第一引数が99字えているものとします。
6は第一引数及び第二引数の合計が99字を超えているものとします。

それから変数nameは絶対に使用しなければならないのでしょうか?(引数に与えられた名前を格納する変数)
問題文にはコマンドの引数が1つ以上与えられた場合、文字列数だけ連結して表示するように、となっているので
1の場合Hello,A.、2の場合Hello,A B.、3の場合Hello,A B C D E.となるようにしたいです。
4の場合は出力としては3と変わらないということになるでしょうか。"A B C D"とEの2つの引数という解釈でいいのでしょうか?
5、6はnameの取得できる文字数を超えるため、エラーを出さないような処理を組む必要があります。長すぎる旨を表示させてエラーの出る処理を飛ばす形になるでしょうか?
name変数はこのプログラムではじめから与えられていて、引数の合計が99字を超えても動作するようにという条件がついています。
そのためこの部分はそのまま使う必要があると解釈しました。
ゆーずぃ さんが書きました:bitter_foxさんの補足をさせて頂くと。
a = "abc";
の形式で代入が出来るのは宣言の時だけです。
ですので、今回の場合はポインタを使うか一文字ずつ代入するかstrcpyなどの関数を使う必要があるということです。
なるほど・・・・・・ありがとうございます

アバター
bitter_fox
記事: 607
登録日時: 14年前
住所: 大阪府

Re: 引数を取って文字列を表示するプログラムの修正

#6

投稿記事 by bitter_fox » 14年前

slei さんが書きました: 問題文にはコマンドの引数が1つ以上与えられた場合、文字列数だけ連結して表示するように、となっているので
1の場合Hello,A.、2の場合Hello,A B.、3の場合Hello,A B C D E.となるようにしたいです。
4の場合は出力としては3と変わらないということになるでしょうか。"A B C D"とEの2つの引数という解釈でいいのでしょうか?
slei さんが書きました:

コード:

    for(i=1; i<3; i++){ /* ループ回数を引数の数で管理 >argc? */
    for(j=0; argv[i][j] != '\0'; j++){
      name[index++]=argv[i][j];
    }
i<3だとargv[1]の文字列とargv[2]の文字列を連結させています。
このプログラムだと1の場合は配列外を参照することになりアプリケーションエラーが発生してしまいます。
また3の場合だと第一引数と第二引数しか連結されないので出力は[Hello, A B.]となってしまいます。
sleiさんの想定する出力にする場合は3のところにコマンド引数の数を持ってくる必要があります。
slei さんが書きました:5、6はnameの取得できる文字数を超えるため、エラーを出さないような処理を組む必要があります。長すぎる旨を表示させてエラーの出る処理を飛ばす形になるでしょうか?
長すぎる旨を表示すると言うのも一つのエラーではないでしょうか?
もし長すぎる旨を表示させたいのであれば引数の文字列長を調べることで実現できます。
文字列長を調べるにはstrlen関数を使用します。
http://www9.plala.or.jp/sgwr-t/lib/strlen.html
slei さんが書きました:name変数はこのプログラムではじめから与えられていて、引数の合計が99字を超えても動作するようにという条件がついています。
そのためこの部分はそのまま使う必要があると解釈しました。
なるほどnameは使用する必要があるんですね。
99字を超えても動作するとありますが、この動作と言うのは全てのコマンド引数が表示されることを指すのかエラーメッセージを出力して(アプリケーションエラーを出さずに)プログラムを終了することを指すのかどちらでしょう?
slei さんが書きました:

コード:

    for(i=1; i<3; i++){ /* ループ回数を引数の数で管理 >argc? */
// ***************** (略)  *****************
    if (i==2){ /* この部分の処理がよくわかっていない */
      name[index] = '\0';
    } else {
      name[index++]=' ';
    }
argv[1]を"A"として、argv[2]を"B"とすると
iが1のときはnameにargv[1]を連結して、まだ後続のargv[2]があるのでnameにさらにスペースを連結してあげます。(上のプログラムのelse)
次にiが2のときはnameにargv[2]を連結します。
for (i = 0; i < 3; i++)よりここでこのループを抜けるのでnameへの連結はなくなります。ですのでnameの末尾に'\0'を付加します。

slei

Re: 引数を取って文字列を表示するプログラムの修正

#7

投稿記事 by slei » 14年前

bitter_fox さんが書きました: i<3だとargv[1]の文字列とargv[2]の文字列を連結させています。
このプログラムだと1の場合は配列外を参照することになりアプリケーションエラーが発生してしまいます。
また3の場合だと第一引数と第二引数しか連結されないので出力は[Hello, A B.]となってしまいます。
sleiさんの想定する出力にする場合は3のところにコマンド引数の数を持ってくる必要があります。
なるほど……コマンド引数の数はargcで取ることになりますかね?
bitter_fox さんが書きました: なるほどnameは使用する必要があるんですね。
99字を超えても動作するとありますが、この動作と言うのは全てのコマンド引数が表示されることを指すのかエラーメッセージを出力して(アプリケーションエラーを出さずに)プログラムを終了することを指すのかどちらでしょう?
99字制限は初めから与えられていたので、異常終了させずにプログラムを終了させられればそれでいいという解釈でした。
文字数制限を変更せずに引数をすべてとることも可能なのでしょうか?
bitter_fox さんが書きました: argv[1]を"A"として、argv[2]を"B"とすると
iが1のときはnameにargv[1]を連結して、まだ後続のargv[2]があるのでnameにさらにスペースを連結してあげます。(上のプログラムのelse)
次にiが2のときはnameにargv[2]を連結します。
for (i = 0; i < 3; i++)よりここでこのループを抜けるのでnameへの連結はなくなります。ですのでnameの末尾に'\0'を付加します。
丁寧な解説、ありがとうございます

アバター
bitter_fox
記事: 607
登録日時: 14年前
住所: 大阪府

Re: 引数を取って文字列を表示するプログラムの修正

#8

投稿記事 by bitter_fox » 14年前

slei さんが書きました: なるほど……コマンド引数の数はargcで取ることになりますかね?
そうなります。
slei さんが書きました: 99字制限は初めから与えられていたので、異常終了させずにプログラムを終了させられればそれでいいという解釈でした。
文字数制限を変更せずに引数をすべてとることも可能なのでしょうか?
字数制限なしに全ての引数を表示できるようにするには次の方法があります。
1.nameなどを使わず引数を直接表示する。

コード:


#include <stdio.h>

int main(int argc, char **argv)
{
	int counter;

	printf("Arg is...\n");
	for (counter = 1; counter < argc; counter++)
	{
		printf("%d : %s\n", counter, argv[counter]);
	}

	return 0;
}

2.動的確保でnameを確保する。
http://www.geocities.jp/ky_webid/c/055.html

slei

Re: 引数を取って文字列を表示するプログラムの修正

#9

投稿記事 by slei » 14年前

ありがとうございます。
ただ今回はnameが与えられているのでそちらを使ったほうがいいと思いますし、プログラムそのものがエラーを吐かなければいいようなので
無理に表示させなくても大丈夫な感じではあります。
bitter_fox さんが書きました: 2.動的確保でnameを確保する。
http://www.geocities.jp/ky_webid/c/055.html
動的確保……授業で少しふれたような記憶はあるんですけどよくわからなかったんですよね……

アバター
bitter_fox
記事: 607
登録日時: 14年前
住所: 大阪府

Re: 引数を取って文字列を表示するプログラムの修正

#10

投稿記事 by bitter_fox » 14年前

slei さんが書きました:ただ今回はnameが与えられているのでそちらを使ったほうがいいと思いますし、プログラムそのものがエラーを吐かなければいいようなので
無理に表示させなくても大丈夫な感じではあります。
であれば、strlen関数で全てのコマンド引数の文字列長を取得してそれが99字を超えているかを判定すればよいでしょう。

コード:


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

int main(int argc, char **argv)
{
	int len = 0;
	int counter;

	for (counter = 1; counter < argc; counter++)
	{
		len += strlen(argv[counter]);
	}
	printf("%d", len);

	return 0;
}

この時の注意点としてはnameには文字列と文字列の間にスペースが入るということも考慮しなければなりません。
>>program.exe aaa bbb
だと第一引数以上の文字列長の合計は6ですが、nameには
"aaa bbb"
と入るので7文字になります。
slei さんが書きました: 動的確保……授業で少しふれたような記憶はあるんですけどよくわからなかったんですよね……
さすがに動的確保を解説するのは大変なので各解説サイトに譲るとして要点としては次のようになります。

コード:


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

int main(int argc, char **argv)
{
	int counter;
	char *str;

	for (counter = 1; counter < argc; counter++)
	{
		str = (char*)malloc((strlen(argv[counter]) + 1) * sizeof(char)); // 1バイトをargv[counter]の文字列長+1個確保
		// 本来は確保に成功したかを判定するべき
		/* if (str == NULL)
		{
			printf("確保できませんでした\n");
			return -1;
		}*/

		strcpy(str, argv[counter]); // コピー
		printf("%s\n", str); // 出力

		free(str); // 解放
	}

	return 0;
}

文字列長+1なのは文字列の末尾に付与されるヌル文字を考慮する必要があるためです。

slei

Re: 引数を取って文字列を表示するプログラムの修正

#11

投稿記事 by slei » 14年前

ありがとうございます、今ちょっと環境がないので明日早速修正を試してみます
bitter_fox さんが書きました: この時の注意点としてはnameには文字列と文字列の間にスペースが入るということも考慮しなければなりません。
>>program.exe aaa bbb
だと第一引数以上の文字列長の合計は6ですが、nameには
"aaa bbb"
と入るので7文字になります。
なるほど……確かに見落としそうな部分です
スペースの数は引数の数から計算できそうですね

アバター
bitter_fox
記事: 607
登録日時: 14年前
住所: 大阪府

Re: 引数を取って文字列を表示するプログラムの修正

#12

投稿記事 by bitter_fox » 14年前

slei さんが書きました: スペースの数は引数の数から計算できそうですね
そうですね。

slei

Re: 引数を取って文字列を表示するプログラムの修正

#13

投稿記事 by slei » 14年前

おそらくこれで完成させることができたと思います、本当にありがとうございました。
つまらない見落としでバグが発生していてもあれですし、修正したコードを張っておきます。

コード:

#include <stdio.h>
#include <string.h>
#define LEN 100

int main(int argc, char *argv[]){
  int i,j;
  int index;
  static char name[LEN];
  int length = 0; /* 引数の長さ */

  for(index = 1; index <argc; index++)
    {
      length += strlen(argv[index]); /* 引数の文字数を取得 */
    }
  length = length + (argc - 2); /* 引数の数-1空白が入る */

  if(length > 100) 
    printf("too long name.\n"); /* 長すぎる場合の処理 */

  else if(argc == 1) /* 引数がない場合nameにeveryoneを入れる */
    strcpy(name, "everyone");

  else{
    index = 0;
    for(i=1; i<argc; i++){ /* ループ回数を引数の数で管理 */
    for(j=0; argv[i][j] != '\0'; j++){
      name[index++]=argv[i][j];
    }
    if (i==argc-1){
      name[index] = '\0';
    } else {
      name[index++]=' ';
    }
   }
  }

  if(length < 100) /* 長すぎてname処理をいれていない場合飛ばす */
  printf("Hello, %s.\n",name);
 
 return 0;
}

閉鎖

“C言語何でも質問掲示板” へ戻る