- ベストアンサー
sscanf(%c)で空文字列なのに読み込んでしまう理由?
Cでデータを1行ずつ読み込んで、 加工して出力するプログラミングをしています。 データ読み込み時のループで、 buff[128]をmemsetで毎回空にしてから、データを読み込んで処理しています。ですが、読み込むデータがbuffの配列より短い場合に、このbuffのデータを下記のようなscanfデータで読み込むとbuffのデータがない場合にもデータを読み込んでしまい困っています。 sscanf(buff,"%8c%8c%8c%8c%8c%8c%8c%8c%8c%8c",f1,f2,f3,f4,f5,f6,f7,f8,f9,f10); 特に、f10にのみこの現象が発生します。 (f1~f9は、ほぼデータがあり、f10はあるときとないときがある) f10の内容を見ると、どうやら、前に使用したデータが読みまれているのですが、memsetでクリアしていますし、実際に、sscanfする前のbuffの中をチェックしましたところ、該当部分は空になっています。もちろん、f1~f10も読み込み前に毎回クリアしています。 buffのクリアの仕方で、 strcpy(buff,"")は、先頭セルのみ空にして、2セル目以降にデータが残ってしまうので、ネットで検索して、memsetを使っていますが、これが原因なのでしょうか? ちなみに、データはわざと%cで読み込んでいます。 どなたかご教示よろしくお願いします。
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
ssacnfの返り値は確認なさったのでしょうか? sscanfは読み込んだフィールドの個数を返します bufが "" なら -1を返すでしょう memset( buf, 0, sizeof(buf) ); memset( f1, 0, sizeof( f1 ) ); memset( f2, 0, sizeof( f2 ) ); memset( f3, 0, sizeof( f3 ) ); memset( f4, 0, sizeof( f4 ) ); memset( f5, 0, sizeof( f5 ) ); memset( f6, 0, sizeof( f6 ) ); memset( f7, 0, sizeof( f7 ) ); memset( f8, 0, sizeof( f8 ) ); memset( f9, 0, sizeof( f9 ) ); memset( f10, 0, sizeof( f10 ) ); sscanf(buff,"%8c%8c%8c%8c%8c%8c%8c%8c%8c%8c",f1,f2,f3,f4,f5,f6,f7,f8,f9,f10); といったコードでお試しください
その他の回答 (4)
- titokani
- ベストアンサー率19% (341/1726)
#3,4です。 >例えば、標準では、8文字×10F=80文字の行で、F9,F10がないとき、1行が8文字×2=16文字少なく64文字になる場合(必ずではない)があって、それでも読み込めるようにしなくてはなりません。この場合わけは、キーワードではなされないので、どちらでも読めるようにする必要があります。 フィールドが8文字なのか10文字なのかは、キーワードごとに固定となるのでしょうか?
お礼
ご教示ありがとうございました。おかげさまで大変助かりました。 今後ともよろしく御願い致します。 (ご連絡が遅くなって大変申し訳ありませんでした。)
補足
ご連絡遅くなって申し訳ありません。 はい、大凡、キーワードによって固定となりますが、8文字のフィールドが多いです。ただし、フィールドによって文字数が変わるキーワードもあります。 いろいろ検討しましたところ、原因は、strcpy(buff,"")でクリアしていたつもりでしたが、前回の値が残ってしまっているためでした。そこで、毎回読み込む前に、buff配列全て(0~80文字まで)と、f1~f10をmemsetで完全にクリアすると今回の問題は解決しました。ただし、今回、50~100万行程度のデータなので、毎回、memsetでクリアすることでかなり時間がかかるようになってしまいました。いろいろとご教示ありがとうございました。
- titokani
- ベストアンサー率19% (341/1726)
#3です。 >入力データは、1フィールド8or10文字で、 >8~10フィールド(変化する)あるのですが、 8or10文字、8~10フィールド(変化する)とは、随時変化する可能性があるのでしょうか? それとも決め打ちで構わないのでしょうか? もし随時変化するとなると、フィールドの区切りの仕様はどのようになっているのでしょうか? 例えば、 01234567890123456789012345678901234567890123456789012345678901234567890123456789 というデータが入ってきた場合、 10文字×8フィールドとするのか、 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 8文字×10フィールドとするのか 01234567 89012345 67890123 45678901 23456789 01234567 89012345 67890123 45678901 23456789 あるいはもっとばらばらになったりするのか、どうなんでしょうか?
補足
説明不足で申し訳ありません。 各データには、キーワードがついていて、それによってフォーマットがきまります。フォーマットは、20~40種類程度 *キーワード 87番 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 *キーワード 38番 F1 F2 F3 F4 F5 F6 F7 F8 など(Fはフィールドを指します。) ただし、フォーマットが決まっていても、必ずしもデータが入っていない(スペース又は\0)ことがあります。特に、右端のフィールドが空で、1行の文字数が短くなっていることがあります。 例えば、標準では、8文字×10F=80文字の行で、F9,F10がないとき、1行が8文字×2=16文字少なく64文字になる場合(必ずではない)があって、それでも読み込めるようにしなくてはなりません。この場合わけは、キーワードではなされないので、どちらでも読めるようにする必要があります。 よろしくお願いします。
- titokani
- ベストアンサー率19% (341/1726)
そもそもsscanfにこだわる意味があるのでしょうか? スピードを気にするなら、memsetなんかよりも、sscanfのほうがはるかに遅いです。 幅が固定なんですから、1バイトづつコピーする関数を作ったほうが話が早そうですが。
補足
ご教示ありがとうございます。 データの長さが変化することと、 空フィールド(空白)があることから、 sscanfの%cを使っていました。 (%d、%fだと空白を飛ばしてしまうため。) もっと良い方法があれば改良したいと思っています。 入力データは、1フィールド8or10文字で、 8~10フィールド(変化する)あるのですが、 高速処理のための1バイトづつコピーする関数を作るとは 例えば、どのようにすればよいのでしょうか? 使用する関数もしくは例をお願いできれば大変ありがたいです。 よろしくお願いします。
- splwtr
- ベストアンサー率16% (75/461)
ANo.1さんの補足的な内容です。 >(f1~f9は、ほぼデータがあり、f10はあるときとないときがある) これを前提にすると、以下の方法もあります。 sscanfの2回目以降の呼び出しで f1~f10を毎回初期化する処理時間を省略するのが狙いです。 if ( (n=sscanf(...)) == 10 ) { /* f1からf10までの結果が得られた時の処理 */ } else if (n==9) { /* f1からf9までの結果が得られた時の処理 */ }
お礼
ご教示ありがとうございます。非常に参考になりました。大変ありがとうございます。 ただし、私の説明不足で大変申し訳ありませんが、実は、f1~f9もたまに、空欄フィールドだったりします(なので%sでなく、%cを使っています。%sだと空欄を飛ばしてしまうので)。その場合に、f1~f9に前回の値が残っていますと、%cを使った場合、前回の値が残ってしまうようですので、やはりf1~f10の初期化は毎回必要かと思っています。memsetより、高速又は簡単に文字配列を初期化(空に)する方法はあるのでしょうか?ネットでも探しましたが、memsetが一番確実かと理解しています。 どうもありがとうございました。
お礼
ご教示ありがとうございます。はい、sscanfの返り値は確認していました。ご教示頂きました内容を見て、f1~f10の初期化が十分ではなかったことが分かりました。今までは、strcpy(f10,"")をしており、f10[9]のf10[0]には""が入っていましたが、その他の部分f10[1-9]に前回読み込み時の内容が入っていました。f1~f10をご教示いただいた内容で毎ループ初期化しましたら、うまくいきました。どうもありがとうございました。 ただし、若干理解しにくい点があります。sscanf(buff,"...%8c",...f9,f10)で、buffが空でも、f10に値が残っているとどうしてその値になってしまうのでしょうか? デバッガで現象を追ってみますと、f10は、9文字の文字配列ですが、どうやら、読み込み時に9文字目に\0が挿入されて、配列の1~8に前回から残っていた文字列が復活してしまうようです???memsetして初期化しますと、1文字目に\0が入っていますので、OKとなりました。