fgetsでハンドルされていない例外が発生

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

fgetsでハンドルされていない例外が発生

#1

投稿記事 by エイドス » 5年前

初めて質問させていただきます。エイドスと申します。
こちらの過去スレッドなども含めて検索したのですが解決しなかったので新しくトピックを建てさせていただきました。

例えば、次のような.txtファイルがあったとします。
ha-34-22 MM-234214 JP AA 34-22
○×○○×○×○××
ha-4-22 MM-234414 JP AA 4-15
○×○○×××
それぞれのデータにおいて例えば×の数を数えて以下のように.csvに出力するとします。
34-22,5
4-15,4

.txtには、全部で5000個近くのデータがあるものとし、以下のようなプログラムをVC++2010EEにて組みました。

コード:

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

int Total, i;
char StrBuf[64], Dust[32], Name[16];
char MainArray[28];
int ArrayNum;
FILE *lf, *sf;

void main(){
	Total = 0;
	i = 0;
	lf = fopen( "Data.txt", "r" );
	sf = fopen( "Score.csv", "w" );

	while( fgets(StrBuf,64,lf) != NULL ){
		if( Total%2 == 0 ){
			sscanf( StrBuf, "%s %s %s %s %s", Dust, Dust, Dust, Dust, Name );
			fprintf( sf, "%s", Name );
		} else if( Total%2 == 1 ){
			memset( MainArray, 0, 28 );
			sscanf( StrBuf, "%s", MainArray );
			i = 0;
			ArrayNum = 0;
			while( MainArray[i] != 0 ){
				if( MainArray[i] == '×' ){
					ArrayNum++ ;
				}
				i++;
			}
			fprintf( sf, ",%d\n", ArrayNum );
		}
		Total++;
		memset( StrBuf, 0, 64 );
	}
	fclose( lf );
	fclose( sf );
}
単純に、1行ずつ読み取って奇数行なら名前を取得、偶数行なら配列を取得して×の数を数えているだけです。
しかし、これを実行したところ、.csv上で2226行目.txt上なら4451行目に当たるデータがおかしくなり、おそらくそのせいで
DNS.exe の 0x5c0df878 (msvcr100d.dll) でハンドルされていない例外が発生しました: 0xC0000005: 場所 0x00000064 を読み込み中にアクセス違反が発生しました。
というエラーがfgets()に関して起こりました。
おかしな出力というのは、
本来であれば
6777-33というデータが出力されなければいけないのにも関わらず、67までしか出力されていないのです。
その行だけ文字数が異常に多いとかそんなことはなく、4450行まででいったん区切り、4451行目以降を別に走らせると少なくとも4451行目は無事通過しますが、今度は51行目ぐらい(本来なら4501行目ぐらい)で同様の現象が起こります。

どうしても分からないのでご教授いただけないでしょうか

アバター
h2so5
副管理人
記事: 2212
登録日時: 9年前
住所: 東京
連絡を取る:

Re: fgetsでハンドルされていない例外が発生

#2

投稿記事 by h2so5 » 5年前

× ひとつで1byteという前提でプログラムを組んでいませんか?
文字コードにもよりますが、○や×は1文字あたり2byteか3byteです。

エイドス

Re: fgetsでハンドルされていない例外が発生

#3

投稿記事 by エイドス » 5年前

h2so5様
今回の例は適当な例で、実際のものとはデータの内容が少し異なります。
○×に関しては、実際のデータは半角の英数字であり、1Byteという前提でプログラムを組んでいます。
○×じゃなくてABにしておけばよかったですね。申し訳ありません。

アバター
みけCAT
記事: 6246
登録日時: 9年前
住所: 千葉県
連絡を取る:

Re: fgetsでハンドルされていない例外が発生

#4

投稿記事 by みけCAT » 5年前

本題とは関係ないと思いますが、

コード:

sscanf( StrBuf, "%s %s %s %s %s", Dust, Dust, Dust, Dust, Name );
これは

コード:

sscanf( StrBuf, "%*s %*s %*s %*s %s", Name );
と書くといいと思います。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

エイドス

Re: fgetsでハンドルされていない例外が発生

#5

投稿記事 by エイドス » 5年前

みけCAT様

ありがとうございます。そのような方法があるとは知りませんでした。

アバター
みけCAT
記事: 6246
登録日時: 9年前
住所: 千葉県
連絡を取る:

Re: fgetsでハンドルされていない例外が発生

#6

投稿記事 by みけCAT » 5年前

とりあえずgcc 4.8.1でコンパイルして適当なケースで実行しましたが、エラーは起きませんでした。

環境
Windows Vista Home Premium SP2 32ビット
Intel(R) Core(TM)2Duo T8100 @2.10GHz 2.10GHz
RAM 4.00GB
添付ファイル
fgets_de_handorusareteinaireigai.zip
テスト用ソースコードとバイナリ、入力データ
(186.38 KiB) ダウンロード数: 20 回
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
nullptr
記事: 239
登録日時: 8年前

Re: fgetsでハンドルされていない例外が発生

#7

投稿記事 by nullptr » 5年前

問題が再現する入力ファイルをアップロードして下さい。

入力ファイルを見ないとなんとも言えませんが、fgetsの使い方にやや疑問があるので書いておきます。


fgetsは「改行を削除しない」ので、入力行が「改行を含めて」64byte(NULを含めて65byte)までである事を確認して下さい。
Dustが32までだとしたら、最も多く見積もって32*4+16+4ほどの文字が来る可能性があるのではないのですか?

fgetsは行の終端(改行)になるか、指定した長さまで読み込んだ分だけのどちらかを読み込みます。
どこかで行の終端まで読み込んでいない場合、Totalの数が一致しません。(トータルが行数を表しているのであれば)Totalをインクリメントする場合は、必ず改行文字が含まれているかを判定して行うべきです。
どこかでずれて、ha-4-22 MM-234414 JP AA 4-15のような行をデータ列として読み込んでしまっている気がします。まあ、なぜfgetsでアクセス違反が起きるかは知りませんけど。
ha-6777-33 MM-234414 JP AA 6777-33
だとして、67はちょうど28~29byte目あたりですね。
 
 
✜ で C ご ✜
: す + 注 :
¦ か + 文 ¦
?
Is the は :
order C++? ✜
     糸冬   
  ――――――――
  制作・著作 NHK
 
 

かずま

Re: fgetsでハンドルされていない例外が発生

#8

投稿記事 by かずま » 5年前

Data.txt の 4449行目~4452行目の 4行のデータを提示してください。

また、次のプログラムの実行結果を教えてください。

コード:

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

int main(void)
{
    char buf[64];
    int n;
    FILE *fp = fopen("Data.txt", "r");
    if (!fp) return 1;
    for (n = 1; fgets(buf, 64, fp); n++)
        if (!strchr(buf, '\n')) printf("%d: %s\n", n, buf);
    fclose(fp);
    return 0;
}

アバター
へにっくす
記事: 628
登録日時: 7年前
住所: 東京都

Re: fgetsでハンドルされていない例外が発生

#9

投稿記事 by へにっくす » 5年前

固定サイズのバッファにscanf系を使用して文字列を入れる場合、ちゃんと長さを指定しましょう。

[迷信] scanf ではバッファオーバーランを防げない

なんかしら文字数を超えてるものがあるとしか思えませんよ。
written by へにっくす

エイドス

Re: fgetsでハンドルされていない例外が発生

#10

投稿記事 by エイドス » 5年前

みけCAT様
VC++2010EEを再インストールしてみてもやはりだめでした

新ゝ月様

ファイルは以下のオンラインストレージにアップしました。3日間の有効期限をかけてありますのであらかじめご了承ください。
http://xfs.jp/WiDAtf
プログラムの一部が異なり、ファイル名と○×判定が実際にはそれぞれ
Data⇒miRNA、×⇒Gとなります。
配列自体は余裕をもった長さにしてありますので、大丈夫だと思います。

かずま様
新ゝ月様宛のところに実データをアップしてありますので上記のリンクからお願いします。
書いていただいたプログラムは現在動かせる環境にないので出来るようになり次第やってみます。

へにっくす様
あとでリンク先を読ませていただきます(さらっと見ただけでは完全に理解できなかったので)が、例の通り文字数がデータによって異なるので長さを指定するのは難しいかもしれません。

アバター
nullptr
記事: 239
登録日時: 8年前

Re: fgetsでハンドルされていない例外が発生

#11

投稿記事 by nullptr » 5年前

Microsoft VisualStudio Professional 2013 Update 4でソースコードを改変せずに実行しましたが、アクセス違反は発生せず、miR-6777-5p,12、miR-6777-3p,3も正常に出力されました。
添付ファイル
Score.csv
(35.28 KiB) ダウンロード数: 20 回
 
 
✜ で C ご ✜
: す + 注 :
¦ か + 文 ¦
?
Is the は :
order C++? ✜
     糸冬   
  ――――――――
  制作・著作 NHK
 
 

can110

Re: fgetsでハンドルされていない例外が発生

#12

投稿記事 by can110 » 5年前

4450行までは問題ありませんが
4998行目「UAGAUCUUUGACUCUGGCAGUCUCCAGG」が28文字ありますので
少なくともMainArrayのサイズでは足りません。

実際のコードと提示されているコードに違いがあるような気もしますが…

エイドス

Re: fgetsでハンドルされていない例外が発生

#13

投稿記事 by エイドス » 5年前

ぼくはくまたいよう 様
ありがとうございます。いざとなったらやっていただいたデータを使わせていただきます。
でも、なんで自分のではだめなんでしょうか・・・・・・

can110様
ありがとうございます。25個ぐらいが最高だと思っていたので、28個あるのは想定外でした。
ただ、うまくいかないのが4451行目なのでそこが原因というわけではたぶんないですよね。
MainArrayは32に直しておきます。
また、提示されているのは例として作ったものなので、実際のコードは
エイドス さんが書きました:プログラムの一部が異なり、ファイル名と○×判定が実際にはそれぞれ
Data⇒miRNA、×⇒Gとなります。
となります。

エイドス

Re: fgetsでハンドルされていない例外が発生

#14

投稿記事 by エイドス » 5年前

かずま さんが書きました:Data.txt の 4449行目~4452行目の 4行のデータを提示してください。

また、次のプログラムの実行結果を教えてください。

コード:

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

int main(void)
{
    char buf[64];
    int n;
    FILE *fp = fopen("Data.txt", "r");
    if (!fp) return 1;
    for (n = 1; fgets(buf, 64, fp); n++)
        if (!strchr(buf, '\n')) printf("%d: %s\n", n, buf);
    fclose(fp);
    return 0;
}
こちらですが、何も表示されずにコンソールアプリケーションが終了しました。
また、特にエラーは検出されませんでした

かずま

Re: fgetsでハンドルされていない例外が発生

#15

投稿記事 by かずま » 5年前

VC++ 2010 Express Edition で実行すると、
確かに fgets() でアクセス違反になりました。

main() の直後に次のコードを挿入すると

コード:

printf("ma=%p\n", MainArray);
printf("28=%p\n", MainArray+28);
printf("lf=%p\n", &lf);

コード:

ma=000E0C20
28=000E0C3C
lf=000E0C3C
と表示されます。MainArray[28] と lf は同じアドレスです。

4998行目の「UAGAUCUUUGACUCUGGCAGUCUCCAGG」を
sscanf() で StrBuf から MainArray に取り込んだとき、
MainArray[28] に '\0' が書き込まれ、FILE構造体へのポインタである
lf が壊れてしまうので、入力処理ができなくなっています。

「うまくいかないのが4451行目なので」というのはどうやって調べたのですか?
止まった時の Total の値を見ると 4997 のはずですが。

アバター
nullptr
記事: 239
登録日時: 8年前

Re: fgetsでハンドルされていない例外が発生

#16

投稿記事 by nullptr » 5年前

No.14で何も表示されずに、とありますが、1で終了していませんか。ちゃんとtxtファイル名を適切に変えて実行しましたか。


> ありがとうございます。25個ぐらいが最高だと思っていたので、28個あるのは想定外でした。
> ただ、うまくいかないのが4451行目なのでそこが原因というわけではたぶんないですよね。
入力データの仕様をはっきり理解していない。曖昧な判断で処理をすることが既に問題だと思います。長さが不定でわからないなら動的に領域を確保するべき。
4451行目が不正に出力される事と、コードの問題は直接でなくとも無関係ではありません。
とりあえず32にするという場当たり的対応をするにしても、関係ないと決めつける前に、実行結果を報告してください。「たぶんないですよね」などという実行結果も伴わない憶測ばかりではいつまでも解決しません。


なぜ2010だとアクセス違反になり、2013だとアクセス違反にならないのかはちょっとわかりません。
2010を持っていないので。
 
 
✜ で C ご ✜
: す + 注 :
¦ か + 文 ¦
?
Is the は :
order C++? ✜
     糸冬   
  ――――――――
  制作・著作 NHK
 
 

アバター
へにっくす
記事: 628
登録日時: 7年前
住所: 東京都

Re: fgetsでハンドルされていない例外が発生

#17

投稿記事 by へにっくす » 5年前

エイドス さんが書きました:ありがとうございます。25個ぐらいが最高だと思っていたので、28個あるのは想定外でした。
ただ、うまくいかないのが4451行目なのでそこが原因というわけではたぶんないですよね。
推測しないでちゃんと思った通りに動くのか、結果を見て判断してください。
プログラムは思った通りに動くのではなく、書いた通りに動くのです。
そこを分かっていないから、「そこが原因というわけではたぶんないですよね。」という言葉が出るんでしょうね。
written by へにっくす

エイドス

Re: fgetsでハンドルされていない例外が発生

#18

投稿記事 by エイドス » 5年前

かずま さんが書きました:「うまくいかないのが4451行目なので」というのはどうやって調べたのですか?
止まった時の Total の値を見ると 4997 のはずですが。
これに関しては、
かずま様
can110 さんが書きました:4450行までは問題ありませんが
4998行目「UAGAUCUUUGACUCUGGCAGUCUCCAGG」が28文字ありますので
少なくともMainArrayのサイズでは足りません。

実際のコードと提示されているコードに違いがあるような気もしますが…
が関係していると思われます。
うまくいかないのが4451行目という理由は
エイドス さんが書きました:しかし、これを実行したところ、.csv上で2226行目.txt上なら4451行目に当たるデータがおかしくなり、
中略
おかしな出力というのは、
本来であれば
6777-33というデータが出力されなければいけないのにも関わらず、67までしか出力されていないのです。
です。

nullptr様
nullptr さんが書きました:No.14で何も表示されずに、とありますが、1で終了していませんか。ちゃんとtxtファイル名を適切に変えて実行しましたか。
txtファイル名を変え、return 0にブレイクポイントを置いて確認してみましたがやはり何も表示されませんでした。
なお、表示はされないものの、nの値は5157、bufの値はCCUGGGGACAGGGGAUUGGGGCAG(最後の行)となっています

また、こちら
nullptr さんが書きました:入力データの仕様をはっきり理解していない。曖昧な判断で処理をすることが既に問題だと思います。長さが不定でわからないなら動的に領域を確保するべき。
4451行目が不正に出力される事と、コードの問題は直接でなくとも無関係ではありません。
とりあえず32にするという場当たり的対応をするにしても、関係ないと決めつける前に、実行結果を報告してください。「たぶんないですよね」などという実行結果も伴わない憶測ばかりではいつまでも解決しません。
はへにっくす様からも同様のご指摘がありましたのでまとめてお返事させていただきますと
弁解にしかなりませんが、(このWikiのページに書かれている情報は古く、間違っているところも多々ありますが)Wikiのようにほとんどの論文で長さが20~25ほどと書かれていますので28にしました。
どれも同じような長さの配列なので少し多めに見積もっておけばわざわざ動的配列にする必要はないと考えたのです。
また、書いたとおりに動くからこそ、実際に動かないところよりも500行近く先のことは一先ずは関係ないと考えていました。

アバター
nullptr
記事: 239
登録日時: 8年前

Re: fgetsでハンドルされていない例外が発生

#19

投稿記事 by nullptr » 5年前

txtファイル名を変え、return 0にブレイクポイントを置いて確認してみましたがやはり何も表示されませんでした。
なお、表示はされないものの、nの値は5157、bufの値はCCUGGGGACAGGGGAUUGGGGCAG(最後の行)となっています
ふむ。おそらく、わざと表示しないようにしたんだと思います。表示されなかったということは、私が最初に危惧していた、「行の終端まで読み込んでいない」訳ではないという事です。同時に、ファイルを行単位で読み込んだだけではアクセス違反が起きないということですから、sscanfやmainArrayの辺りが原因である可能性が高いです。
弁解にしかなりませんが、(このWikiのページに書かれている情報は古く、間違っているところも多々ありますが)Wikiのようにほとんどの論文で長さが20~25ほどと書かれていますので28にしました。
どれも同じような長さの配列なので少し多めに見積もっておけばわざわざ動的配列にする必要はないと考えたのです。
なるほど、それはわかりました。それで、肝心の実行結果が書かれていないように見受けられます。32に訂正して、実行結果は変わりましたか。
 
 
✜ で C ご ✜
: す + 注 :
¦ か + 文 ¦
?
Is the は :
order C++? ✜
     糸冬   
  ――――――――
  制作・著作 NHK
 
 

エイドス

Re: fgetsでハンドルされていない例外が発生

#20

投稿記事 by エイドス » 5年前

nullptr さんが書きました:肝心の実行結果が書かれていないように見受けられます。32に訂正して、実行結果は変わりましたか。
申し訳ありません。書くのを忘れていました。
32に変更しても結果は変わりませんでした。

アバター
nullptr
記事: 239
登録日時: 8年前

Re: fgetsでハンドルされていない例外が発生

#21

投稿記事 by nullptr » 5年前

…全く変わらなかったのですか?
念のため、その訂正したソースコードと、visualstudioの出力ウィンドウをコピーして載せてもらえませんか。

全く変わらないとなると…どういう事なんですかね。正しく動かないにせよ何かしら変化はあると思ったんですが。
 
 
✜ で C ご ✜
: す + 注 :
¦ か + 文 ¦
?
Is the は :
order C++? ✜
     糸冬   
  ――――――――
  制作・著作 NHK
 
 

アバター
nullptr
記事: 239
登録日時: 8年前

Re: fgetsでハンドルされていない例外が発生

#22

投稿記事 by nullptr » 5年前

あとでリンク先を読ませていただきます(さらっと見ただけでは完全に理解できなかったので)が、例の通り文字数がデータによって異なるので長さを指定するのは難しいかもしれません。
見落としていましたが、これは違います。
sscanfで%sの間に指定しろと書いてあるのはバッファーサイズであって、入力サイズではありません。28なら27を、32なら31を書けばいいのです。こちらも試してみてください。
32に訂正する際に、memsetも修正するのを忘れないで下さい。
オフトピック
memsetが悪さしてる…訳ではない?2010と2013で動作が違う理由を少し考えてみましたが、やはり未定義動作に起因している気がします。
 
 
✜ で C ご ✜
: す + 注 :
¦ か + 文 ¦
?
Is the は :
order C++? ✜
     糸冬   
  ――――――――
  制作・著作 NHK
 
 

かずま

Re: fgetsでハンドルされていない例外が発生

#23

投稿記事 by かずま » 5年前

生成された Score.csv を見ると、サイズは 30897バイト。
内容は、2225行目が「miR-6777-5p,12」、2226行目が「miR-67」です。
出力が 2226行あるから、入力は 4452行読み込んだと考えて、
そこで落ちたと考えたんですね。
それは大きな間違いです。

fprintf(sf, ...); を実行しても、
それがその時点でファイルに書き込まれるわけではありません。
4KB の大きさのバッファにため込まれるのです。
そのバッファが満杯になったところで write() が呼び出されます。
出力ファイルはテキストモードで open されているので、
'\n' の前に '\r' をつけて、WriteFile API で実際にファイルに出力されます。
追加された '\r' は 2225個。
7回 WriteFile を実行したようで、4096 x 7 + 2225 = 30897 となります。

2箇所の fprintf(sf, ...); のあとに fflush(sf); を付けて実行してみると、
今度は本当に fprintf() の結果がすぐにファイルに書き込まれます。
できた Score.csv のサイズは 35084バイト。
最後の行 2499行目は、「miR-7161-3p,7」です。
入力を 4998行読み込んで処理したわけです。

fflush() がないと、4998行目までの出力はバッファにため込まれています。
4998行目の「GUAGGGGCGUCCCGGGCGCGCGGG」で lf が壊され、
4999行目を読み込もうとした fgets() で落ちます。
バッファの内容はファイルに吐き出されません。
だから、ファイルのサイズは 30897バイト(2226行)なのです。
バッファの内容がファイルに書き出されるのは、
fflush() または fclose() を実行した時です。
fclose() を忘れても exit() で fclose() が実行されます。
exit() がなくても main() から return すると、exit() が呼ばれます。

char MainArray[29]; にすると、Score.csv は 36131バイト(2578行)となり
入力ファイル miRNA.txt(5156行)を全部処理したことになります

エイドス

Re: fgetsでハンドルされていない例外が発生

#24

投稿記事 by エイドス » 5年前

遅くなり申し訳ありません。
nullptr さんが書きました:32に訂正する際に、memsetも修正するのを忘れないで下さい。
こちらは忘れていましたが、32に修正しても駄目だったので
もう一度アンインストール⇒再起動⇒再インストール⇒再起動してみたところ、32でうまくいきました。
(うまく行った後にこちらを見たのでこの時点ではmemsetは28のままですが)
怖いので、28では試していません。
アンインストール後の再起動の際にWindowsUpdateのインストールが行われていたので、もしかしたらそこと何か競合を起こしていたのでしょうか?
かずま さんが書きました:出力が 2226行あるから、入力は 4452行読み込んだと考えて、
そこで落ちたと考えたんですね。
それは大きな間違いです。

fprintf(sf, ...); を実行しても、
それがその時点でファイルに書き込まれるわけではありません。
知りませんでした。勉強になりました。ありがとうございます。


ご迷惑をおかけしましたが、(理由はとりあえず分からないもののプログラムが動きましたし、)配列の要素数にミスがあったこととfprintfの仕様について勘違いをしていたことが分かりましたのでこれで解決とさせていただきます。
ありがとうございました。

閉鎖

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