###前提・実現したいこと
c++のstd::string型の文字列に含まれる改行コードのみを高速で取り除きたい
c++11, c++14の機能は使用せず実現したい
###発生している問題・エラーメッセージ
改行を含む文字ファイルをstd::stringに読み込み、findメソッドで改行コードを先頭から検索→見つかるたびにreplaceメソッドで空文字に置換するような処理を作成しました。
元データが10万行を超えるような長いものになるととても時間がかかるのですが、より良い方法はないでしょうか。
よろしくお願いいたします。
###該当のソースコード
c++
1//あらかじめstd::string targetStrに対象の文字列が読み込まれているものとする。 2const std::string CRLF = "\r\n"; 3const std::string CR = "\r"; 4const std::string LF = "\n"; 5std::string::size_type pos = 0; 6while(pos = targetStr.find(CRLF, pos), pos != std::string::npos) { 7 targetStr.replace(pos,CRLF.length(), ""); 8 pos += CRLF.length(); 9} 10pos = 0; 11while(pos = targetStr.find(CR, pos), pos != std::string::npos) { 12 targetStr.replace(pos,CR.length(), ""); 13 pos += CR.length(); 14} 15pos = 0; 16while(pos = targetStr.find(LF, pos), pos != std::string::npos) { 17 targetStr.replace(pos,LF.length(), ""); 18 pos += LF.length(); 19}
###補足情報(言語/FW/ツール等のバージョンなど)
cまたはc++
ただしc++11, c++14の機能は使用せず実現したい
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答6件
0
ベストアンサー
std::string::replace
はコストが高い処理なので、きついのだと思います。また、CRの処理とLFの処理だけでCRLFも削除されるので必要ありません。ということで、別途、stringを新たに作る形にして、最後にコピーすれば良いのでは無いでしょうか?ただし、メモリは2倍必要になるので、その点はご注意を。
C++
1void deleteNl(std::string &targetStr) 2{ 3 const char CR = '\r'; 4 const char LF = '\n'; 5 std::string destStr; 6 for (std::string::const_iterator it = targetStr.begin(); 7 it != targetStr.end(); ++it) { 8 if (*it != CR && *it != LF) { 9 destStr += *it; 10 } 11 } 12 targetStr = destStr; 13}
おまけ
C++11が使えるならムーブにすることで、ちょっとだけ速くなるような気がします。
C++
1void deleteNl2(std::string &targetStr) 2{ 3 const char CR = '\r'; 4 const char LF = '\n'; 5 std::string destStr; 6 for (const auto c : targetStr) { 7 if (c != CR && c != LF) { 8 destStr += c; 9 } 10 } 11 targetStr = std::move(destStr); 12}
投稿2016/05/08 14:49
編集2016/05/08 15:06総合スコア21737
0
C++98/03縛りでもBoostライブラリの利用OKなら、1行で実現できます。速度は未計測ですが、replaceを3回呼び出すよりは速いと期待したいところ...
C++
1#include <boost/range/algorithm_ext/erase.hpp> 2#include <boost/algorithm/string/classification.hpp> 3 4void remove_crlf(std::string& s) 5{ 6 boost::remove_erase_if(s, boost::is_any_of("\r\n")); 7}
投稿2016/05/09 01:36
総合スコア6191
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
How about this?
C++
1void remove_crlf(std::string& s) 2{ 3 size_t i, j; 4 for( i = 0, j = 0; i < s.size(); i++ ){ 5 if( s[i] != '\r' && s[i] != '\n' ){ 6 s[j] = s[i]; 7 j++; 8 } 9 } 10 s.resize(j); 11}
Without resizing, this string is not null-terminating.
投稿2016/05/09 02:36
編集2016/05/09 02:47総合スコア1722
0
「CRまたはLF」を除外してみた:
C++
1#include <iostream> 2#include <string> 3#include <algorithm> 4 5using namespace std; 6 7int main() { 8 string str = "abc\r\ndef\r\nghi\r\n"; 9 string::iterator last = 10 remove_if(str.begin(), str.end(), 11 [](char ch) { return ch == '\r' || ch == '\n'; }); 12 str.erase(last, str.end()); 13 cout << '[' << str << "]\n"; 14}
投稿2016/05/09 00:07
総合スコア16612
0
10万行ともなるとコピーコストも莫迦にできませんね。文字列の途中の文字を削除しようとすると、その位置にそれ以降を文字列終端までコピーすることになり、結果、同じ部分を何度もコピーするので非効率です。
raccyさんのように別の文字列に移し替えるようにすれば最小限のコピーで済みます。
raccyさんとは別のやり方で作ってみました。文字列領域を若干多めに取ることになりますが、コピーは最小限だと思います。
※reserveで領域サイズを決め打ちしているので、もしかしたら領域の節約になっているかもしれません。
非C++11版
C++
1struct IfCrLf 2{ 3 bool operator ()(char ch) 4 { 5 return ch == '\r' || ch == '\n'; 6 } 7}; 8 9struct IfNotCrLf 10{ 11 bool operator ()(char ch) 12 { 13 return ch != '\r' && ch != '\n'; 14 } 15}; 16 17std::string str; 18str.reserve(targetStr.length()); 19auto iter = targetStr.begin(); 20while(iter != targetStr.end()) 21{ 22 auto iter2 = std::find_if(iter, targetStr.end(), IfCrLf()); 23 str.append(iter, iter2); 24 iter = std::find_if(iter2, targetStr.end(), IfNotCrLf()); 25} 26targetStr.swap(str);
C++11版
C++
1auto ifcrlf = [](char ch){return ch == '\r' || ch == '\n';}; 2std::string str; 3str.reserve(targetStr.length()); 4auto iter = targetStr.cbegin(); 5while(iter != targetStr.cend()) 6{ 7 auto iter2 = std::find_if(iter, targetStr.cend(), ifcrlf); 8 str.append(iter, iter2); 9 iter = std::find_if_not(iter2, targetStr.cend(), ifcrlf); 10} 11targetStr.swap(str);
投稿2016/05/08 15:29
編集2016/05/08 15:43総合スコア5944
0
こんにちは。
string::find_first_of()でCRとLFの最初に出現したものをサーチしつつ、リプレースすれば1パスで済むので1.5倍強くらいの速度になるだろうと思います。
【追記】
文字は削除するだけなので、前方へコピーすれば済む筈です。
C++
1std::size_t n=0; 2std::string::iterator dst=targetStr.begin(); 3std::string::iterator end=targetStr.end(); 4for (std::string::iterator src=targetStr.begin() ; src != end; ++src) 5{ 6 if ((*src == '\r') || (*src == '\n')) 7continue; 8 9 *dst++=*src; 10 ++n; 11} 12targetStr.resize(n);
最後のresize()で変なコピーしないかちょっとだけ心配ですが、縮める方向なので管理領域を書き換えるだけと期待。
投稿2016/05/08 14:03
編集2016/05/08 16:02総合スコア23272
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/05/08 14:35
2016/05/08 14:51
2016/05/08 14:54
2016/05/08 15:05
2016/05/08 15:24
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/05/08 15:08