みんなの「教えて(疑問・質問)」にみんなで「答える」Q&Aコミュニティ

こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

続jファイルに文字列を書く

あれから後一歩と言うところにきました

たぶんmallocのバグだと思いますがチャットのシステムを作ろうとしているのですが
読み込み時にエラーになります

ソースの注目部分を見てほしいのですが、
注目部分はbuf+1 にすると 文字列が1文字ずつ消えていくバグになり
bufにすると 3行目を書き込んだ時点で3行目がの頭の部分の文字列がおかしくなり4行目を書き込もうとするとエラーになります

ちゃんと動作するにはどのように書けば良いですか?

---ソース---
#include <stdio.h>

void main(void){

FILE *fp;
char *tm[1000];
char buf[400];
int i=1,sei;

fp= fopen("now.txt","w+");
fprintf(fp,"もも");
fclose(fp);

//何で最初に書き込んでるんだ?
//という突っ込みがあるでしょうが本当に作りたいプログラムは最初にファイルに書き込まないといけないためです。

fp= fopen("now.txt","r");
while( fgets( buf, 400, fp ) != NULL ){
tm[0]=(char*)malloc(strlen(buf)+1);
strcpy(tm[0], buf);
}

fclose(fp);

fp =fopen("moto.txt","r");
while( fgets( buf, 400, fp ) != NULL ){
tm[i] = (char*)malloc(strlen(buf+1));
strcpy(tm[i], buf+1); //ここを注目

if(i<999){
i++;
}
}

fclose(fp);

if(i<=1000){
sei=i;
}

else{
sei=1000;
}


fp =fopen("moto.txt","w");

for(i=0;i<sei;i++){
if(i==0){
fprintf(fp,"%s\n",tm[0]);
}

else{
fprintf(fp,"%s",tm[i]);
}
}

}

---now.txt---
もも


---moto.txt---
オレンジ
みかん

投稿日時 - 2012-12-06 14:56:21

QNo.7832396

暇なときに回答ください

質問者が選んだベストアンサー

>+1を入れると\0がはいるんじゃないんですか?

strlen()は「引数で渡されたアドレスから」文字列終端までの長さを返します。
'\0'が入る領域を用意するのであれば、「strlen()の返した値」に+1する必要があります。
ですので、"123456789"と9文字入っているアドレスの"2"が入っているところから数えたら8文字しかありませんよね?
掲示されたコードは"2"から数え始めてね。と指定しているのです。
# そして、"2"からコピーしてね。としてコピーしています。

>fp= fopen("now.txt","r");
>while( fgets( buf, 400, fp ) != NULL ){
>tm[0]=(char*)malloc(strlen(buf)+1);
>strcpy(tm[0], buf);
>}
では、正しく「文字数+1」となっているのに、問題の箇所では「先頭2文字目からの文字数」となっているのはなぜですか?

>つまりstrcpy(tm[i], buf+3);
>にすれば何とかなるのですね

そりゃエラーにはならないかも知れませんが、それが想定している動作なんですか?
掲示されたファイルの例(Shift-JIS想定)だと……
1回目で(3行目がヘンになっているかも知れない。半角カナになっている為)
---moto.txt---
激塔W
(空行)
ゥん

2回目の実行で
---moto.txt---

(空行)
(空行)
(空行)
になりますかね。
# 3行目が空行かゴミが出るかは状況次第のギャンブル。

>想定外と申されましても試行錯誤して書き換えているので想定そのものができません

では期待動作はなんなんでしょうか?

投稿日時 - 2012-12-06 17:29:51

補足

>>>+1を入れると\0がはいるんじゃないんですか?

strlen()は「引数で渡されたアドレスから」文字列終端までの長さを返します。
'\0'が入る領域を用意するのであれば、「strlen()の返した値」に+1する必要があります。

あ、そういえばそうですね
勘違いしてました
+1入れろと書いてあったので間違えたようです


>>ですので、"123456789"と9文字入っているアドレスの"2"が入っているところから数えたら8文字しかありませんよね?
掲示されたコードは"2"から数え始めてね。と指定しているのです。
# そして、"2"からコピーしてね。としてコピーしています。

ポインターの位置換えですね
だから1行目が消えたのですね

>fp= fopen("now.txt","r");
>while( fgets( buf, 400, fp ) != NULL ){
>tm[0]=(char*)malloc(strlen(buf)+1);
>strcpy(tm[0], buf);
>}
では、正しく「文字数+1」となっているのに、問題の箇所では「先頭2文字目からの文字数」となっているのはなぜですか?

見落としてかいたのか試行錯誤しているうちに偶然に書き換えてしまったからです
指摘していただきありがとうございます。

>つまりstrcpy(tm[i], buf+3);
>にすれば何とかなるのですね

<<そりゃエラーにはならないかも知れませんが、それが想定している動作なんですか?
掲示されたファイルの例(Shift-JIS想定)だと……
1回目で(3行目がヘンになっているかも知れない。半角カナになっている為)
---moto.txt---
激塔W
(空行)
ゥん

2回目の実行で
---moto.txt---

(空行)
(空行)
(空行)
になりますかね。
# 3行目が空行かゴミが出るかは状況次第のギャンブル。

想定の意味が自分が思ったようにこうしたいと言う理想の処理と言う意味であれば
想定外ですよ
申し訳ありませんね
想定の意味がプログラムを書き換えた後で必ずこうなる事を知っている事が想定と言う意味だと勘違いしていました

>想定外と申されましても試行錯誤して書き換えているので想定そのものができません

>>では期待動作はなんなんでしょうか?


期待している動作

1回目の動作で
もも
みかん
オレンジ

をmoto.txtの内容にする

2回目の動作で追加したい文字がぶどうなら
ぶどう
もも
みかん
オレンジ

をmoto.txtの内容にする

3回目の動作で追加したい文字がいちごなら
いちご
ぶどう
もも
みかん
オレンジ

をmoto.txtの内容にする

期待している動作はこんな感じです

投稿日時 - 2012-12-06 19:12:38

お礼

って私が書いたプログラムをわざわざ修正して2回できるようにしたのですね
お手数おかけして申し訳ありません

このままコメントがなければベストアンサーにさせえていただきますね

投稿日時 - 2012-12-06 19:21:42

ANo.5

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(5)

ANo.4

Wr5

>再三指摘されているfgets()での改行コードに

失礼しました。
再三指摘されていたのは別人でした。
たぶんお友達だと思いますけど。

投稿日時 - 2012-12-06 15:49:05

補足

nanaka2222の事でしたら私自身ですよ

間違えてログアウトしたらパスワードを覚えていなくて戻れなくなりましてnanaka2223jを新たに作りました
ちなみに私に友達はいません

投稿日時 - 2012-12-06 18:48:42

お礼

よく見たらNO2~5さんまでWr5さんでしたね
いつもお世話になってありがとうございます

また変な事を聞いていたらすみませんね

今までのおかげでだいぶチャットが完成まじかになりましたよ
機能もいろいろ増えましたしね。
最もミニゲームも加える予定なのでチャットができたら完成って訳ではないんですけどね

今までは一人用のチャットを作れるようにしていたのですが
複数人いた時用のプログラムに以降予定です

ここまでアドバイスしていただき本当にありがとうございました

投稿日時 - 2012-12-06 19:46:04

ANo.3

Wr5

>ちゃんと動作するにはどのように書けば良いですか?

どういう動作が期待している動作なのか?
が不明です。

とりあえず、問題にしている箇所でバッファオーバーランを発生させないようにするのであれば、

tm[i] = (char*)malloc(strlen(buf+1));
strcpy(tm[i], buf+1);

tm[i] = (char*)malloc(strlen(buf)); // buf文字分しかないけど次のコピーで1文字カットするから'\0'の分はある。
strcpy(tm[i], buf+1);
とすべきでしょう。

まあ、これで読み込んだ行の先頭1文字を削除できるか?は、ファイル次第ですけど。
Shift-JISなテキストファイルで1文字目に日本語とか書かれていたら…ヘンになるかも知れませんし、
ASCIIで記述されたテキストファイルならたぶん正しく1文字目が削除されるでしょう。
# 現状のmoto.txtだとたぶん壊れます。



再三指摘されているfgets()での改行コードに関しては…まぁ、問題ないのでしょう。
私的には気持ち悪い処理内容ですが……。
# 部下や教えている生徒がこういうコード書いてきたら、なぜそうなるのかキチンと説明を求めるレベル。
「1行目の次に空行を挿入して、それ以降の行は空行を挿入しない処理です。」以外の回答があるのかは謎。

投稿日時 - 2012-12-06 15:39:52

補足

期待している動作

1回目の動作で
もも
みかん
オレンジ

をmoto.txtの内容にする

2回目の動作で追加したい文字がぶどうなら
ぶどう
もも
みかん
オレンジ

をmoto.txtの内容にする

3回目の動作で追加したい文字がいちごなら
いちご
ぶどう
もも
みかん
オレンジ

をmoto.txtの内容にする

期待している動作はこんな感じです



<<まあ、これで読み込んだ行の先頭1文字を削除できるか?は、ファイル次第ですけど。
Shift-JISなテキストファイルで1文字目に日本語とか書かれていたら…ヘンになるかも知れませんし、
ASCIIで記述されたテキストファイルならたぶん正しく1文字目が削除されるでしょう。
# 現状のmoto.txtだとたぶん壊れます。

逆です1文字目が消えちゃうからバグなのです
一文字も消えずにそのまま表示したいのです


<<再三指摘されているfgets()での改行コードに関しては…まぁ、問題ないのでしょう。
それは修正しましたよ

<<私的には気持ち悪い処理内容ですが……。
私から見れば順番に処理画家かれてるので見やすいですが、他の方が同様のプログラムを作る場合なんて全くないのでどうして気持ち悪いのかわかりません。ごめんなさい。

ただ他の人の書いたソースは見ずらいと言うことでしたら私も同様にみづらいなと思うこともあるのでお互いさまなのではないでしょうか?


<<# 部下や教えている生徒がこういうコード書いてきたら、なぜそうなるのかキチンと説明を求めるレベルなぜそうなるのかと言われましても順番に処理を書いていってバグなどがおきたり新しい処理を追加していったらいつの間にか自然とそうなったとしか、後はそのため試行錯誤を繰り返したらそうなったとしかいえません


<<「1行目の次に空行を挿入して、それ以降の行は空行を挿入しない処理です。」以外の回答があるのかは謎

そういう風に答えれば良いんですね
でしたら最初の一行目は一度別のファイルに文字列をすべて書き込んで文字列をつなげた一つの文字列にしました
その理由は漢字、ひらがな、#等の文字をstrcatで連結させようとするとめちゃくちゃバグるからです
そのっため別のファイルに書き込んだ文章を再び呼び出してtm[0]に格納しました

その次にmoto.txtにかかれた文字列を順次tm[i]に格納して文字列をつなげて表示したかったのですがバグが発生しました
3行目で最初の文字が消えるだけでなく、4行目を書き込もうとしたらエラーになる
そのため試行錯誤してバグを取り除こうとした結果、最初の一文字つつきえるとはいえチャットのように何行も書き込める方法を発見ちょっと喜びました
その後バグがなくなるよう試行錯誤したのですが発見できずこちらで相談させていただきました

このような回答でよろしいでしょうか?

投稿日時 - 2012-12-06 18:43:58

ANo.2

Wr5

malloc()のバグではなく、「使い方」の問題でしょう。
今時malloc()にバグが潜在するようなライブラリなんてないでしょうし。

>tm[i] = (char*)malloc(strlen(buf+1));
の意図ってなんでしょう?
なぜ「bufのアドレスに+1」する必要があるんですか?
文字列終端の'\0'の分が確保されませんから、次の
>strcpy(tm[i], buf+1); //ここを注目
で、『予定通り』バッファオーバーランを実行します。
# tm[i] = (char*)malloc(strlen(buf)+1);
# だったら、「bufに入っている文字列をコピーするのに必要なサイズ」が確保されますけど…
# 掲示されたコードでは「bufに入っている文字列を2文字少なくコピーできるサイズ」しかありません。

バッファオーバーランで想定外の箇所を書き換えているのですから…
>文字列が1文字ずつ消えていく
>3行目を書き込んだ時点で3行目がの頭の部分の文字列がおかしくなり4行目を書き込もうとするとエラー
という動作になっても想定内のハズです。

投稿日時 - 2012-12-06 15:28:59

補足

>>tm[i] = (char*)malloc(strlen(buf+1));
の意図ってなんでしょう?
なぜ「bufのアドレスに+1」する必要があるんですか?
文字列終端の'\0'の分が確保されませんから、次の
>strcpy(tm[i], buf+1); //ここを注目
で、『予定通り』バッファオーバーランを実行します。

+1を入れると\0がはいるんじゃないんですか?

# 掲示されたコードでは「bufに入っている文字列を2文字少なくコピーできるサイズ」しかありません。
つまりstrcpy(tm[i], buf+3);
にすれば何とかなるのですね

<バッファオーバーランで想定外の箇所を書き換えているのですから…

<という動作になっても想定内のハズです。

想定外と申されましても試行錯誤して書き換えているので想定そのものができません

投稿日時 - 2012-12-06 17:02:49

ANo.1

他のところと見比べればわかっていいはずなんだが....

「malloc して strcpy」する関数を, なぜ作らない?

投稿日時 - 2012-12-06 15:16:50

補足

そういう関数が必要ですか?

うまくできるようあたまっをひねって見ます

投稿日時 - 2012-12-06 16:47:04