Borland C++ Builder 5 を使っています。
30万件以上のレコードが格納されたCSVファイルを読込むプログラムを作っています。
1件当りのレコード長は可変です。(MAX値は余裕を見て200バイトくらい。)
1件ごと決められた処理をする必要がありますので、次のようなソースを書きました。
while(fgets(buf,200,fp31) != NULL){
//CSVの分解とデータ処理
}
しかし、さすがに30万件は時間がかかります。
まとめてドッカンと読込む方法もあろうかと思うのですが、1件ごと処理をするためにはどうしたら良いか分かっていません。
何かうまい方法はないものでしょうか?
ご指導いただければ幸いです。
No.5ベストアンサー
- 回答日時:
CSVはまともに処理しようと思うと、fgets等を使って行単位で読み込むべきではありません。
というのは、フィールド内に改行(CRLF)が含まれるケースがあるからです。この場合は二重引用符でエスケープされます。
ちなみに、CSVの仕様(RFC4180)に合致していることを期待してよいのであれば、改行は必ずCRLFですし、ASCII以外の文字が含まれることも想定する必要はありません。
標準ライブラリのストリームは、(少なくともBorland C++ Compilerに限れば)最も効率がよいであろうサイズでバッファリングされているはずですので、そこを調整しても大した影響は出ないでしょう。
それより、パーサーを高速化するほうが得策かと思います。
参考URL:http://www.ietf.org/rfc/rfc4180.txt
ありがとうございました。変身が遅くなり申し訳ありません。
いくつかのテストをしてみました。
単純に、fgets()で行単位に読むか、fread()でブロック単位に読むかの比較です。
実際の容量75Mくらいのファイルを使いました。
行単位で2秒、ブロック単位で1秒でした。
(少数以下の秒は表示していない。)
目で見ている感覚では、ブロック単位は1秒以下という感じです。
以上はローカルディスクでの話です。
これがネットワーク経由で読み込むと、行単位は25秒くらいになりました。
ただ、今回の仕組みはローカルでの運用ですから、まあ、ほとんど変わりない、という結論でも良いかもしれません。
なお、CSVについては、市販のあるパッケージソフトが吐き出すものです。
それぞれのフィールドは、そのパッケージに入力した項目や、マスター類の項目がほとんどです。
入力項目に複数行を認めるものはありません。
従ってシロート判断ですが、フィールド内に改行は無い、と考えています。
ただし日本語フィールドはあります。
結論としては、改造はやめようと思います。
No.4
- 回答日時:
fopenのmodeに"S"とか_openのoflagに_O_SEQUENTIALを付けて先読みに期待させる
ありがとうございました。
fopenのmodeの"s"というのが分かりませんでした。
fopen(FLNM,"rs")としてみましたが、コンパイルは通ったのですが、実行時にOPENできない、というエラーになりました。
せっかくアドバイスいただいたのに生かすことが出来ずに申し訳ありませんでした。
No.3
- 回答日時:
setvbuf(fp, NULL, _IOFBF, 1000000);
とりあえずこれをfopen直後に入れて、パフォーマンスの変化を確認してください。詳細はsetvbufで検索するなり、手元の資料なりで調べてみてください。
No.2
- 回答日時:
レコードサイズが今度余り増えないようなら一気に読み込んでしまっても
いいのですが、そうでないのであれば、1MB~数MBくらいのバッファに
一度途中まで読み込み、1文字ずつ解析をします。
1回の読み込みではまだファイルにデータが残っていることが多く、
その場合メモリにある最後の行のレコードも途中である可能性もあるので
そこの繋ぎ部分は注意して作る必要がありますが、この方法なら
それほどファイル読み込みも負荷にはならない可能性が高いですし、
メモリ的に大丈夫でしょう。
で、1行毎の解析ですが、まず分割という処理の必要性が疑問です。
頭から解析し、1つ1つの","で区切られた文字を見て、改行があればそこで1レコード終了になります。
なので、明示的に分割して何かするという処理は要らないです。
>この場合の改行コードは16進表記で「0d0a」ですよね
0x0aだけかもしれませんし、0x0dだけかもしれません。
0x0d/0x0aと連続で来ることを期待して作るとバグを生むかもしれません。
>前回の出現位置から今回の出現位置までをbufにCOPYする。
ファイルからメモリに読み込んだ段階でそこにテキストがあるので
これは要りません。(ASCII->UNICODE変換など変換があるなら別ですが)
PROMETHEUSさん、ありがとうございました。
>で、1行毎の解析ですが、まず分割という処理の必要性が疑問です。
これは、単純に今のプログラム(CSVを分解してデータ処理を行う)がそのまま利用できる、というだけの理由です。
なるほど、頭からCSVを分割してしまう発想はありませんでした。
> 0x0aだけかもしれませんし、0x0dだけかもしれません。
> 0x0d/0x0aと連続で来ることを期待して作るとバグを生むかもしれません。
そうなんですか、Windous系の場合(今はNTFSですが)すべて0x0d/0x0aと連続で来ると思っていました。
ちょっと厄介ですね。
>その場合メモリにある最後の行のレコードも途中である可能性もあるので
>そこの繋ぎ部分は注意して作る必要がありますが、
これは漠然とそんなことも考えていました。
やはり自分で何とかしないといけないのですね。
やはりスピードを上げるとなると、厄介なことが多いですね。
No.1
- 回答日時:
30万件ですかー
単純に200×30万=57MB弱
結構大きいね
単純に考えるなら一行ずつ読み込むのではなく
いったんファイルの内容全部をメモリに読み込む方法が考えられます。
その後、改行コードで分割して、一行ずつ解析する処理になるかと。
※ファイルにアクセスするのって結構時間がかかるんです。なるべく少なく。。
phoenix343さん、ありがとうございました。
メモリーに読込んだデータを改行コードで分割する方法ですが、この場合の改行コードは16進表記で「0d0a」ですよね?
(1)メモリーの頭から1バイトずつ「0d0a」が出現するか判定する。
(2)出現したら、前回の出現位置から今回の出現位置までをbufにCOPYする。
(3)bufのデータ処理を行った後、今回の出現位置から後方向に「0d0a」を探す。
というような方法が思い浮かびます。
ちょっとスマートでないような気もします。
まあ、泥臭い方法の方が確実で誰にでも分かりやすい、ということはあると思うのですが・・・。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- Excel(エクセル) PowerQueryに詳しい方教えてください(Office365) 1 2022/07/24 21:11
- Excel(エクセル) 【VBA】指定フォルダに格納中のテキストファイルをエクセルで処理し結果のエクセルを新規フォルダに保存 1 2022/03/25 14:19
- Excel(エクセル) Excel Powerクエリーの質問、行数指定は可能でしょうか? 2 2022/08/22 12:54
- システム CSVファイルのマッピング処理の省力化 1 2022/11/24 00:01
- その他(プログラミング・Web制作) Sikulix2.0.5(Jython2.7.3)でcsvを読込WEB検索条件にpasteで文字化け 1 2023/03/31 11:02
- その他(プログラミング・Web制作) データ解析ソフトRでのファイル入力read.csvがエラーになります 7 2022/03/27 22:11
- Excel(エクセル) エクセルのVBAについて とあるサイトのコードを参考に、CSVの文字化けを直すVBAを作成しているの 7 2022/11/04 14:15
- PHP PHPでCSVを出力するさいに、ループの中で前の行の値を変更したい 3 2022/10/27 17:44
- Ruby pandasでsqlite3にテーブル作成・追加・読み出しでindexの取り扱い方教えてください 5 2023/03/08 09:57
- その他(プログラミング・Web制作) pythonでクラスで複数のメソッドを利用する方法 2 2022/04/15 04:17
このQ&Aを見た人はこんなQ&Aも見ています
関連するカテゴリからQ&Aを探す
おすすめ情報
このQ&Aを見た人がよく見るQ&A
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
freadでファイルを読み込んだ際...
-
C/C++ ファイル入出力操作 (長...
-
ファイルポインタが動かない・・・
-
テキストファイルの行数を取得...
-
fgetsで2行目から文字化け
-
C言語初心者の質問失礼します。
-
どんなプログラムを書いても指...
-
マイクラでPythonのプログラミ...
-
バッファとは何ですか
-
ソースから参照しているOCXの一...
-
jarファイルとjava.exeの関連付...
-
jarの開き方を教えてください。
-
c/c++ ビルドしたにもかかわら...
-
printfだと出力されるのにfprin...
-
フォルダ内の特定 拡張子のファ...
-
xismoについてです!開こうとす...
-
VBA でメモ帳へ保存する際の保...
-
VBスクリプトのWshShell.Runに...
-
バイナリエディタのつかいかた
-
コンポーネント`MSCOMM32.cox'...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
ファイル内のデータを1行削除...
-
バイナリファイルをテキストフ...
-
fgetsで2行目から文字化け
-
テキストファイルの行数を取得...
-
c言語 2つのファイルを行ご...
-
C言語での改行コードの扱いにつ...
-
VBSで指定行に挿入
-
改行までの一文字ずつのファイ...
-
【VB.Net】バイト型配列に読み...
-
freadとfwrite
-
巨大なテキストファイル(可変...
-
0バイトファイルの作成
-
fopenで開いたファイルのサイズ...
-
fopen(書き込みモード)でファイ...
-
winsock recvでの文字化け
-
ファイルサイズ指定し、ファイ...
-
fortranで文字列を読み込む際の...
-
【C言語】テキスト読み込みの行...
-
C言語での採番について
-
VS2010 MFC CStdioFileについて
おすすめ情報