公式アカウントからの投稿が始まります

入力: This is a pen.
入力文字列:[ This is a pen. ]
出力文字列;[ pen. a is This ]
―――――――――――――――――――
上に示したように単語の順番をさかさまにして表示するプログラムを作りました。しかし、今のプログラムでは、最初に空白が来たり、空白が連続すると正しく表示できません。どこを修正すればいいのか教えてください。
--------------------------
#include <stdio.h>

#define BUF 1024

void out(int,char);

int main(void){
char in[BUF]; /* 入力された文字列を保存する */
char out[BUF]; /* inoput の内容を単語逆順にして保存する*/
int wordcount; /* 文字カウント */
char *in_ptr,*out_ptr; /* それぞれの文字列の文字をさすポインタ */
int i,j,a,b; /* 繰り返し用カウンタ */

/* メッセージの表示+文字列の入力 */
printf("入力: ");
fgets(in, sizeof(in), stdin);

/* ポインタの初期化 */
in_ptr = in;
out_ptr = out;

/* 単語数の初期化 */
wordcount = 0;

/* 単語数を数える作業 */
while(*in_ptr != '\n'){
if(*in_ptr!=' '&&(*(in_ptr+1)==' '||*(in_ptr+1)=='\n')){
wordcount++;
}
in_ptr++;
}

/* メッセージの表示+単語数の表示 */
printf("\n文字数のカウント開始\n");
printf("文字数のカウント終了: %d単語\n",wordcount);

printf("\n入力文字列:[ %s ]\n", in);
printf("出力文字列;[ ");

/* 単語逆順処理作業 */
b=0;
for(a=0;a<wordcount;a++){
j=0;
b++;
in_ptr = in;

for(i=0;i<(wordcount-b);i++){
while(*in_ptr!=' '){
j++;
in_ptr++;
}
in_ptr++;
}
/* ポインタを単語の頭に持っていく */
in_ptr= in + (j+(wordcount-b));

/* inをoutにコピー */
while(*in_ptr!='\0' && *in_ptr!=' ' && *in_ptr!='\n'){
*out_ptr = *in_ptr;
in_ptr++;
out_ptr++;
}
if(*in_ptr==' '||*in_ptr=='\n'||*in_ptr=='\0'){
*out_ptr = ' ';
out_ptr++;
}
}
printf("%s]\n",out);
return 0;
}

A 回答 (4件)

★回答者No.2です。


・前回のアドバイスをソースにしてみました。
 参考にして下さい。

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

#define BUFF_SIZE 1024
#define WORD_SIZE 100

// メイン関数
int main( void )
{
 char *word[ WORD_SIZE ];
 char in[ BUFF_SIZE ];
 char out[ BUFF_SIZE ];
 char *pi = in;
 char *po = out;
 int n, pos;
 
 // 入力
 printf( "入力:" );
 fgets( in, sizeof(in), stdin );
 *strchr(in,'\n') = '\0';  // 簡単改行除去(*)
 
 // (1)単語の先頭をポインタ配列にセット
 for ( pos = 0 ; ; pos++ ){
  if ( pos >= WORD_SIZE ){
   printf( "単語の数が多すぎます。\n" );
   return 1;
  }
  while ( isspace(*pi) ){  // 空白文字を読み飛ばし
   pi++;
  }
  if ( (*pi == '.') || (*pi == '\0') ){
   break;
  }
  word[ pos ] = pi;   // 単語の先頭を記憶
  
  while ( isalpha(*pi) ){  // 英字文字を読み飛ばし
   pi++;
  }
 }
 // (2)ポインタ配列の最後から順番に単語をコピー
 for ( n = (pos - 1) ; n >= 0 ; n-- ){
  pi = word[ n ];    // 単語の先頭を取り出す
  
  while ( isalpha(*pi) ){  // 連続する英字文字をコピー
   *po++ = *pi++;
  }
  *po++ = ' ';    // 間の空白文字をコピー
 }
 // 最後にピリオド文字を追加
 po[ -1 ] = '.';
 po[ 0 ] = '\0';
 // 表示
 printf( "入力単語数:%d\n", pos );
 printf( "入力文字列:[%s]\n", in );
 printf( "出力文字列:[%s]\n", out );
 return 0;
}
以上。
    • good
    • 0

ソースコード読んでみました。


やっていることは、後ろの単語からout[]にコピーしているのですが、次の問題点があります:

コピーする単語の位置を得る処理
in_ptr = in + (j + (wordcount - b));
ぱっと見て、jは単語の先頭位置かと思ったのですが、それでは(wordcount -b)の説明がつかない。結局、
j: 空白を除いた場合の文字の位置("this is a pen."の場合、'p'の位置を示す7)
(wordcount - b): その単語に遭遇するまでに存在するであろう空白の数

空白が2個ずつある"this__is__a__pen."の場合、、(_を空白と読み替えてください)
そうすると、空白が2個ずつ入っていた場合、外側のループ数は4回固定なのに対し、空白は6個です。空白を見つけるとそれで1単語のコピー処理ループを実行するため、最後まで処理を行いません。

また、空白を見つけると、その次の位置に単語が入っていると仮定してコピーを行うので、結局空白をそのまま出力してしまっています。

"this__is__a__pen."(2個ずつ空白)の出力結果は
" is__this "
となります。
単語の認識は"this", "", "is", ""となるため、
・最初の空白は、""のあとに追加される空白
・"is"と"this"の間の空白2個は、"is"の後に追加される空白と、""の後に追加される空白
・最後の空白は、"this"のあとに追加される空白

また、out[]の最後にヌル文字を入れていないので、文字列を出力するさい、後ろにゴミが表示されることがあります。

修正といっても、半分作り直しに近いです。

・bを削除する
・jは配列位置そのままとする
・out[]を削除する(直接出力するため、必要ない)
・in_ptr, out_ptrは削除する

アルゴリズムとしては、
jを文字列の最後尾位置に設定し、後ろから前に走査していく方法です。

最後の単語位置を取得し、その単語を出力
ループ開始
 次の単語位置を取得し、単語位置が0以上なら、
  空白とその単語を出力
 そうでなければ、
  ループ脱出
ループ終了

となります。
一番最後の単語とそれ以前の単語は分けて考えなければなりません。
なぜなら、「単語が見つかったら、出力し、空白を出力する」というアルゴリズムだと、一番先頭の単語が見つかった後も空白を出してしまうからです。
"this is a pen."だと"pen. ", "a ", "is ", "this "(thisの後に空白がついてしまう)となり、最後に空白がついてしまいます。
最後の単語はそのまま出力し、その後は「空白を出力→単語を出力」だと、
"pen.", " a", " is", " this"となり、最後の空白はなくなります。

プログラムは次のようになります:

j = strlen(in) - 1;
j = get_next_word(j); //次の単語の先頭位置を得る
print_next_word(j); //その位置の単語を出力する

while (1) {
 j = get_next_word(j);
 if (j >=0) {
  printf(" ");
  print_next_word(j);

 } else {
  break;

 }
}
printf(".");

get_next_word()は改行文字の扱い、print_next_word()は改行、空白、ピリオドの扱いに注意が必要です。

最後に、がんばってください。
    • good
    • 0

★コンパイルしてみました。


>しかし、今のプログラムでは、最初に空白が来たり、空白が連続すると正しく表示できません。
 ↑
 本当だ。正しく表示されないね。
>どこを修正すればいいのか教えてください。
 ↑
 コードを読みたくないよ。
 だから新しいアルゴリズムをアドバイスします。
 (1)単語の先頭をポインタの配列に代入。
  (a)先頭のスペースを読み飛ばす
  (b)単語の初めをポインタ配列にセット
  (c)連続する英字を読み飛ばす
  (d)(a)へ戻る
  (e)『\0』が現れるまで繰り返す
 (2)ポインタ配列の最後から順番に単語をコピー。
  (a)ポインタ配列の最後から先頭に向け処理
  (b)ポインタ配列の単語を別領域へコピー
  (c)連続する英字をコピーしてその次に間のスペースをコピー
  (d)ポインタ配列の1つ前を処理
  (e)ポインタ配列の先頭を処理するまで繰り返す
 (3)画面に表示。
  (a)普通にprintfで表示
・上記の新しいアルゴリズムで作り変えてみるのはどうですか?
 あとスペースの読み飛ばしには isspace() 関数を使ってみるのはどう?
 英字文字の判定では isalpha() 関数を使う。
 でもピリオド文字はどうしましょう?
 工夫して下さい。
・以上。
    • good
    • 0

本当に質問者さんが作ったプログラムですか?


このプログラムが書けるのなら質問内容くらい分けないと思うのですが…。

課題丸投げは禁止ですよ。

この回答への補足

このプログラムは本当に自分で作りました。
なんとか作れったっていう感じです。
プログラムングのヒントがあったのでここまでは作れたのですが、質問内容に関してはヒントがなかったので、わかりませんでした。
すいませんが、教えていただけると嬉しいです。

補足日時:2007/12/16 14:23
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!