読者です 読者をやめる 読者になる 読者になる

Gobble up pudding

プログラミングの記事がメインのブログです。

MENU

C/C++で行数を数えるときの注意とwcコマンド

Programming C++
スポンサードリンク

f:id:fa11enprince:20150730082908j:plain
C/C++を使っているとファイルを読んで、メモリ上にデータを格納したいときに
メモリをどのくらい確保する必要があるか調べるのに行数を数えることが多いかと思います。
だいたいは以下の様なコードを書かれたことが多いのではないのでしょうか?
私はよほどパフォーマンス上の問題がない限りC++流儀のものを使ったほうがいいと思いますが……。
下記は行数を調べてその分だけ配列を確保して文字列をメモリに読み込んでいるコードです。
100万行のテキストを読み込んでいます。
てかvector使え……うわなにをするやめ…くぁwせdrftgyふじこ

※補足
ただしこの方法はファイルフォーマットがガチガチに決まっていて、
一行あたりの最大文字列が規定されているときのみに使える方法です。
もし、そうではなくて特にルールがないファイルだとすれば
うまくバッファリングを考慮したコードを書くしかありません。
もしくは多少メモリ使用量と処理速度を犠牲にして
C++でSTLを使って楽にファイル読込をするか…です。
(とはいってもSTLを使ったコードより簡単には
速いコードを書けるとは思いませんが…)
そういうものが対象であればむしろ言語をC++以外で選ぶのが吉です。
C++でcha型に一気に読み込む場合であればSTLではないですが
std::istream::readあたりが便利

参考

そこで、問題なのが行数を数える方法。
基本的にfgetsで読み飛ばしてカウントするのがいいと思われるのですが、
ネットにはfgetcで読み込んで改行がみつかったらカウントというのが結構引っかかります。
こんなやつ

この方法は良くないと思います。なぜならめちゃくちゃ遅いからです。fgetsはなにかインチキ?して速いんです。
Linuxのコマンドのwcって速いです。数千万行でもすぐ結果が返ってきます。
ということで気になってwcのソースコードをめちゃくちゃあさ~く調査しました。
まずは読みやすいBSDのコード
https://code.google.com/p/retrobsd/source/browse/trunk/src/cmd/wc.c?r=702
おそろしく簡単なコードですね。
ってかfgetc使ってる!
f:id:fa11enprince:20140416225955g:plain
そんなバカな……
ここちがうな…検索するとここが見つかっちゃいました。
と、苦戦しながらさがしました。どこにあるのかわからなかったぞー。

FreeBSD wc

http://svnweb.freebsd.org/base/stable/10/usr.bin/wc/wc.c?revision=256281&view=markup
システムコール使ってるみたい。
僕の普段使ってるwcはgnuのcoreutilsのwcでした。
読みにくいことで有名?なgnuのソースコード見つけてきました。

GNU wc

http://git.savannah.gnu.org/gitweb/?p=coreutils.git;a=blob;f=src/wc.c;h=4909d9fe855e82ac5ea5c652dfe3dd03734fce78;hb=HEAD
うん、なんかいろいろやってるっぽい…
safe-read.hとか見りゃいいんだろうけど見る気力がなくて力尽きましたw
ちなみにですが、100万行読むのにfgetsとfgetcでやったときでどれくらい違うのかというと…Cygwin上でしかもtimeコマンドで測ったのでちょい微妙な計測結果ですが、

fgetc fgets
real 0m7.556s 0m0.205s
user 0m7.453s 0m0.156s
sys 0m0.000s 0m0.015s

こんなんでした。
ちなみに最適化オプション変えてもほとんど両者とも変わらず……。
そういえば思い出しましたが、fgetなんちゃら系とかって純粋なCはint型じゃないとダメなんだった。
…つーか、毎度I/Oをコールしてたらそりゃ遅いに決まってる。
と、まぁ薄っぺらい内容の投稿でした。
f:id:fa11enprince:20140416230127p:plain