前提・実現したいこと
c言語についての質問です。
文字配列 char text[]="It is good to see you. Thank you for coming."; を宣言し、
textの文字列を単語ことに分割して標準出力するというプログラムを作成しています。
ルールとしてスペースやピリオドは出力せず、出力する単語は一行に一単語ずつ '['と']'
で囲んで出力するというプログラムです。
また、単語間は必ず一文字分のスペースで区切られているという前提で作成してよいとする。
strtok等の文字列を分割する関数を用いずに作成する。外部プログラムの呼び出しもNG
という前提です。
発生している問題・エラーメッセージ
str[0] : [It ]
str[1] : [is ]
str[2] : [good ]
str[3] : [to ]
str[4] : [see ]
str[5] : [you. ]
str[6] : [Thank ]
str[7] : [you ]
str[8] : [for ]
str[9] : [coming.]
続行するには何かキーを押してください . . .
これは、プログラムを実行したときの実行結果です。
しかし、先生から スペースやピリオドを除いてください.ということでした。
いろいろサイトを見て試してみたのですがどうしても回答に行きつきません。 どうしたらよいでしょうか?
初めての質問なので不慣れな点があるかと思いますが、よろしくお願いします
該当のソースコード
c言語
ソースコード#include<stdio.h># define _SPACE 0x20
int main(void)
{
int i;
char text[]="It is good to see you. Thank you for coming.";
char str[10][256+1];
char *s, *d;
s = text; for(i=0; i<10;i++) { d = str[i]; while ((*d++ = *s++) != _SPACE) { } *d = '\0'; } for(i=0; i<10; i++) { printf("str[%d] : [%s]\n",i,str[i]); }
}
試したこと
いろいろなサイトを見て、# define _SPACE 0x20を追加してみたもののうまくいきませんでした。
補足情報(FW/ツールのバージョンなど)
ここにより詳細な情報を記載してください。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

回答7件
0
ベストアンサー
せっかくここまでやったのだから、出来るだけ活かしましょう。
一見してまず、区切りの空白が出力されちゃってるのが問題、かな。
C
1 while ((*d++ = *s++) != _SPACE) 2 { 3 } 4 *d = '\0'; 5}
ここ、一所懸命(そして地道に、端折らずに)考えてみて下さい。例えば冒頭のIt[空白]でどう動くかトレース。
whileのカッコ内で、
dすなわちstr[0][0]にsすなわちtext[0]の'I'をコピー
d++でdはstr[0][1]、s++でsはtext[1]を指すようになります。
で、'I'は_SPACEとは異なるのでループ継続して、
dすなわちstr[0][1]にsすなわちtext[1]の't'をコピー
d++でdはstr[0][2]、s++でsはtext[2]を指すようになります。
で、't'は_SPACEとは異なるのでループ継続して、
dすなわちstr[0][2]にsすなわちtext[2]の' 'をコピー
d++でdはstr[0][3]、s++でsはtext[3]を指すようになります。
で、' 'は_SPACEと等しいのでループを継続しません。
そして、whileループを抜けたところで*dすなわちstr[0][3]に'\0'を代入しています。
さて、ここでstr[0]はどうなっていますか。
'I' 't' ' '
空白を見つけてループを終了はしましたが、コピーしたものが入っています。これが、あなたの出力に空白が含まれてしまう原因。
対応の方法はいくつか考えられますが、例えば
C
1 while ((*d++ = *s++) != _SPACE) 2 { 3 } 4 *--d = '\0'; 5}
としてみるとか。whileを抜けたときにdは' 'の次の文字を指しているので、一つ戻して'\0'を上書きしています。あるいは、
C
1 while ((*d = *s++) != _SPACE) 2 { 3 d++; 4 } 5 *d = '\0'; 6}
とすれば、ループの中でdをインクリメントしますから、ループを抜けたときにはdは' 'を指しているので'\0'を上書きします。
つづいてピリオドも出力しないようにしましょう。
まず、
C
1#define __SPACE ' ' 2#define __PERIOD '.'
としましょうか。_+アルファベット大文字というシンボルは諸般の事情(C++では予約語とされる)で使わないほうがいいと思うのでちょっと変えます。また、空白なら' 'をそのまま記述すべきかと思います。だって、あなたが扱いたいのは' 'でしょ。0x20と書き換える意味はないじゃないですか。(ASCIIコードじゃない文字コードにも対応出来る、とも言えるし...超レアだけど)
続いて、今は空白を識別している場所を拡張します。
C
1 while ( (*s != __SPACE) && (*s != __PERIOD) ) 2 { 3 *d=*s; 4 d++; 5 s++; 6 } 7 *d='\0'; 8 s++;//sをインクリメントしておかないといけない。
比較を二度行うので、sのインクリメントをwhileの式中でやってしまうわけにはいきません。((*d = *s) != __SPACE) && (*s++ != __PERIOD) )
とすれば二度目の比較のあとでのインクリメントになると考えるかもしれませんが、落とし穴があって...&&は、第一項が偽だと結果が決まってしまうので第二項を評価しないことになっています。そうなると、空白だったときとピリオドだったときとでインクリメントが行われる行われないの違いが出てしまいます。
これでどうなるか...
TEXT
1str[0] : [It] 2str[1] : [is] 3str[2] : [good] 4str[3] : [to] 5str[4] : [see] 6str[5] : [you] 7str[6] : [] 8str[7] : [Thank] 9str[8] : [you] 10str[9] : [for]
ちょっと惜しい感が...str[6]の[]がなんだかなぁ。で、最後のcomingが押し出されちゃった。原文では". "のところ。'.'で単語が終わって、さらに' 'で単語が終わるので、空の単語ができちゃったんですね。
じゃあ、' 'とか'.'がなくなるまで、単語の開始を送っておきましょう。
C
1 //空白と.の間は文字を送る 2 while( (*s==__SPACE) || (*s == __PERIOD)){ 3 s++; 4 } 5 //ここに来た時は空白や.ではない 6 while ( (*s != __SPACE) && (*s != __PERIOD) ) 7 { 8 *d=*s; 9 d++; 10 s++; 11 } 12 *d='\0'; 13 s++;
とりあえずこんなもので。
「表示」だけすればいいのだから、別に各々の単語を個別の文字列に格納する必要はない、とか与える文字列(単語数)が変わったら、とかいい出すと流石に根本から考え直しになるので、ここまで。(そういうのを考えてみるのはとてもいい勉強になりますけれど)
投稿2019/07/06 02:02
総合スコア7733
0
ビギナは仕方ないかも知れないが、
すべてのことをmain一本でやろうとするのは悪いクセだ。
必要な部品(関数)を考え、それを作って組み上げよ。
C
1#include <stdio.h> 2#include <stdbool.h> 3 4/* str が c を含むなら true を返す */ 5bool contains(const char* str, char c) { 6 while ( *str != '\0' ) { 7 if ( *str == c ) return true; 8 ++str; 9 } 10 return false; 11} 12 13/* str 中、delim に含まれない文字が見つかった位置を返す */ 14char* skip_delim(char* str, const char* delim) { 15 char* ptr; 16 for ( ptr = str; *ptr != '\0'; ++ptr ) { 17 if ( !contains(delim, *ptr) ) break; 18 } 19 return ptr; 20} 21 22/* str 中、delim に含まれる文字が見つかった位置を返す */ 23char* span_delim(char* str, const char* delim) { 24 char* ptr; 25 for ( ptr = str; *ptr != '\0'; ++ptr ) { 26 if ( contains(delim, *ptr) ) break; 27 } 28 return ptr; 29} 30 31/* 32 * 定義した skip_delim, span_delim を用いて単語を切り出す 33 */ 34int main() { 35 char text[] = "It is good to see you. Thank you for coming."; 36 const char* delim = " ."; /* 空白とピリオドが単語の区切り */ 37 38 char* end = text; 39 while ( *end != '\0' ) { 40 char* begin = skip_delim(end, delim); /* 単語はbeginから始まる*/ 41 if ( *begin == '\0' ) break; /* 終端に達したらおしまい */ 42 end = span_delim(begin, delim); /* end:単語の区切りを見つけ */ 43 *end = '\0'; /* そこに'\0'を置くことで文字列を終端すれば */ 44 printf("[%s]\n", begin); /* beginが切り出すべき単語(の先頭)となる */ 45 ++end; /* endの次から繰り返す */ 46 } 47 return 0; 48}
投稿2019/07/05 23:14
総合スコア16612
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
一文字ずつ...
c
1#include <stdio.h> 2#define FLAG_ON (-1) 3#define FLAG_OFF (0) 4int main(void) { 5 char text[]="It is good to see you. Thank you for coming."; 6 int count=0, inWord=FLAG_OFF; 7 for(char *p=text; *p!='\0'; p++) { 8 if(*p == ' ' || *p == '.') { 9 if(inWord) { printf("]\n"); inWord=FLAG_OFF; } 10 continue; 11 } 12 if(!inWord) { printf("str[%d] : [", count++); inWord=FLAG_ON; } 13 printf("%c", *p); 14 } 15}
投稿2019/07/06 17:42
総合スコア13318
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

0
前回回答者です。
>ルールとしてスペースやピリオドは出力せず、出力する単語は一行に一単語ずつ '['と']'
>で囲んで出力するというプログラムです。
「スペースやピリオドは出力せず」のところが、気になりました。
?とか#とか数字なども出力しないのでしょうか。
「1単語は、AZ又はazのみで構成される文字とし、それ以外の文字は、出力しない」ということであれば
以下のようにしてください。
C
1#include<stdio.h> 2# define _SPACE 0x20 3 4int main(void) 5{ 6 int i; 7 char text[]="It is good to see you? Thank you for coming."; 8 char *s, *d; 9 10 s = text; 11 d = s; 12 i = 0; 13 while(1) 14 { 15 if (*s == '\0') break; 16 if ((*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z')) 17 { 18 s++; 19 }else{ 20 *s = '\0'; 21 if ( d != s ) 22 { 23 i++; 24 printf("[%d] : [%s]\n",i,d); 25 } 26 s++; 27 d = s; 28 } 29 } 30 if ( d != s ) 31 { 32 i++; 33 printf("[%d] : [%s]\n",i,d); 34 } 35} 36
投稿2019/07/06 01:29
総合スコア5533
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/07/06 20:17
2019/07/06 21:59
2019/07/06 22:07
2019/07/06 22:19
2019/07/06 22:33
2019/07/06 22:59
2019/07/06 23:01 編集
2019/07/07 00:36
2019/07/07 00:59
2019/07/07 01:53
2019/07/07 02:31

0
char str[10][256+1];
としていますが、単語が10個であることを前提にしています。
もし、どうしても使いたいならchar str[1000][256+1];のように十分大きくとるべきです。
それなら単語1000個まで対応可能です。
又、for(i=0; i<10;i++)も、単語が10個であることを前提に作っています。
よって、strを使用しない方法にしました。
以下のようになります。
C
1#include<stdio.h> 2# define _SPACE 0x20 3 4int main(void) 5{ 6 int i; 7 char text[]="It is good to see you. Thank you for coming."; 8 char *s, *d; 9 10 s = text; 11 d = s; 12 i = 0; 13 while(1) 14 { 15 if (*s == '\0') break; 16 if (*s == _SPACE || *s == '.' ) 17 { 18 *s = '\0'; 19 if ( d != s ) 20 { 21 i++; 22 printf("[%d] : [%s]\n",i,d); 23 } 24 s++; 25 d = s; 26 }else{ 27 s++; 28 } 29 } 30 if ( d != s ) 31 { 32 i++; 33 printf("[%d] : [%s]\n",i,d); 34 } 35} 36
以下、実行結果です。
[1] : [It]
[2] : [is]
[3] : [good]
[4] : [to]
[5] : [see]
[6] : [you]
[7] : [Thank]
[8] : [you]
[9] : [for]
[10] : [coming]
投稿2019/07/06 00:01
編集2019/07/06 00:40総合スコア5533
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
sscanf は使っていいのでしょうか?
C
1#include <stdio.h> 2 3int main(void) 4{ 5 char text[] = "It is good to see you. Thank you for coming."; 6 char str[256]; 7 int i = 0; 8 while (1) { 9 int n = 0; 10 sscanf(text + i, "%255[^a-zA-Z]%n", str, &n); 11 i += n; 12 if (sscanf(text + i, "%255[a-zA-Z]%n", str, &n) != 1) break; 13 i += n; 14 printf("[%s]\n", str); 15 } 16}
別解
やっぱり、sscanf は文字列を分割する関数になるかもしれないので、
別のやり方にします。
C
1#include <stdio.h> 2#include <ctype.h> 3 4int main(void) 5{ 6 char text[] = "It is good to see you. Thank you for coming"; 7 unsigned char c; 8 int n = 0, i = 0, j = 0; 9 for (; c = text[i]; i++) 10 if (isalpha(c)) !n && (n = 1, j = i); 11 else if (n) n = !printf("[%.*s]\n", i - j, text + j); 12 if (n) printf("[%.*s]\n", i - j, text + j); 13}
isalpha(c) の c は EOF 以外の負の値であってはならないので
unsigned char c; にしています。
n は、単語を表示しているので後で改行が必要ですというフラグです。
追記
出力する単語は一行に一単語ずつ '['と']' で囲んで出力する
この条件を満たしていなかったのでコードを修正しました。
投稿2019/07/05 21:38
編集2019/07/06 01:25総合スコア8222
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
時間がないのでヒントだけ。
単語を判定するには、ctype.hをincludeしてisalnumで判断しましょう。
ctype.h
「追記」・・・遅くなりましたがw
c
1#include <stdio.h> 2#include <ctype.h> 3 4int main(void) 5{ 6 const char text[] = "It is good to see you. Thank you for coming."; 7 // 8 const char *cptr = text; 9 // 10 char buf[128][32]; // 数字は適当 11 int index = 0; 12 while (*cptr) { 13 // 英字以外をスキップ 14 while (!isalpha(*cptr) && *cptr) { 15 cptr++; 16 } 17 // 終端の時は終了 18 if (*cptr == '\0') { 19 break; 20 } 21 // 英字だけをコピー 22 char *dptr = buf[index]; 23 while (isalpha(*cptr)) { 24 *dptr++ = *cptr++; 25 } 26 *dptr = '\0'; // 行末 27 index++; 28 } 29 // 30 for (int i = 0; i < index; i++) { 31 printf("str[%d] : [%s]\n", i, buf[i]); 32 } 33 // 34 return 0; 35}
実行結果
text
1usr ~/Project/test % ./a.out 2str[0] : [It] 3str[1] : [is] 4str[2] : [good] 5str[3] : [to] 6str[4] : [see] 7str[5] : [you] 8str[6] : [Thank] 9str[7] : [you] 10str[8] : [for] 11str[9] : [coming] 12usr ~/Project/test %
投稿2019/07/05 21:05
編集2019/07/06 19:45総合スコア6851
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/07/06 15:14

あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。