c++ - 関数 - std::string substr
std:: stringをトリミングする最良の方法は何ですか? (20)
C ++ 11では正規表現モジュールも提供されていますが、これはもちろん先行または後続のスペースをトリミングするために使用できます。
多分このような何か:
std::string ltrim(const std::string& s)
{
static const std::regex lws{"^[[:space:]]*", std::regex_constants::extended};
return std::regex_replace(s, lws, "");
}
std::string rtrim(const std::string& s)
{
static const std::regex tws{"[[:space:]]*$", std::regex_constants::extended};
return std::regex_replace(s, tws, "");
}
std::string trim(const std::string& s)
{
return ltrim(rtrim(s));
}
私は現在、私のプログラムですべてのstd::strings
を右にトリムするために次のコードを使用しています:
std::string s;
s.erase(s.find_last_not_of(" \n\r\t")+1);
それは正常に動作しますが、失敗する可能性のあるいくつかのエンドケースがあるのだろうかと思います。
もちろん、優雅な選択肢と左利きの解答は歓迎します。
c ++ 11:
int i{};
string s = " h e ll \t\n o";
string trim = " \n\t";
while ((i = s.find_first_of(trim)) != -1)
s.erase(i,1);
cout << s;
出力:
hello
空の文字列でもうまく動作します
Boostの文字列アルゴリズムを使うのが最も簡単です:
#include <boost/algorithm/string.hpp>
std::string str("hello world! ");
boost::trim_right(str);
str
は今"hello world!"
。 trim_left
とtrim
もあり、両面をトリミングします。
上記の関数名のいずれかに_copy
サフィックスを追加すると、関数はtrim_copy
などの文字列のトリムコピーを返します。
例えば、 trim_copy_if
ような関数名に_if
サフィックスを追加すると、空白だけでなくカスタム述語を満たすすべての文字をトリミングすることができます。
Cplusplus.comハッキング
string choppa(const string &t, const string &ws)
{
string str = t;
size_t found;
found = str.find_last_not_of(ws);
if (found != string::npos)
str.erase(found+1);
else
str.clear(); // str is all whitespace
return str;
}
これはnullの場合も同様です。 :-)
@ Bill the Lizardの答えに基づく私の解決策。
入力文字列に空白だけが含まれている場合、これらの関数は空文字列を返します。
const std::string StringUtils::WHITESPACE = " \n\r\t";
std::string StringUtils::Trim(const std::string& s)
{
return TrimRight(TrimLeft(s));
}
std::string StringUtils::TrimLeft(const std::string& s)
{
size_t startpos = s.find_first_not_of(StringUtils::WHITESPACE);
return (startpos == std::string::npos) ? "" : s.substr(startpos);
}
std::string StringUtils::TrimRight(const std::string& s)
{
size_t endpos = s.find_last_not_of(StringUtils::WHITESPACE);
return (endpos == std::string::npos) ? "" : s.substr(0, endpos+1);
}
あなたがやっていることは上品で頑丈です。 私は長い間同じ方法を使いましたが、私はまだもっと速い方法を見つける必要があります:
const char* ws = " \t\n\r\f\v";
// trim from end of string (right)
inline std::string& rtrim(std::string& s, const char* t = ws)
{
s.erase(s.find_last_not_of(t) + 1);
return s;
}
// trim from beginning of string (left)
inline std::string& ltrim(std::string& s, const char* t = ws)
{
s.erase(0, s.find_first_not_of(t));
return s;
}
// trim from both ends of string (left & right)
inline std::string& trim(std::string& s, const char* t = ws)
{
return ltrim(rtrim(s, t), t);
}
トリムする文字を入力することで、空白以外の文字をトリムする柔軟性と、トリムする文字だけをトリムする効率が得られます。
ここに私が思いついたものがあります:
std::stringstream trimmer;
trimmer << str;
trimmer >> str;
ストリーム抽出は空白を自動的に排除するので、これは魅力的です。
あまりにも清潔でエレガントな、私は自分自身を言う場合。 ;)
ここに私のバージョンがあります:
size_t beg = s.find_first_not_of(" \r\n");
return (beg == string::npos) ? "" : in.substr(beg, s.find_last_not_of(" \r\n") - beg);
これは、 back()
とpop_back()
追加により、C ++ 11でより簡単に行うことができます。
while ( !s.empty() && isspace(s.back()) ) s.pop_back();
これはどうですか...?
#include <iostream>
#include <string>
#include <regex>
std::string ltrim( std::string str ) {
return std::regex_replace( str, std::regex("^\\s+"), std::string("") );
}
std::string rtrim( std::string str ) {
return std::regex_replace( str, std::regex("\\s+$"), std::string("") );
}
std::string trim( std::string str ) {
return ltrim( rtrim( str ) );
}
int main() {
std::string str = " \t this is a test string \n ";
std::cout << "-" << trim( str ) << "-\n";
return 0;
}
注:私はまだ比較的新しいC ++です。私がここにいるのであれば、私を許してください。
これを試して、それは私のために働く。
inline std::string trim(std::string& str)
{
str.erase(0, str.find_first_not_of(' ')); //prefixing spaces
str.erase(str.find_last_not_of(' ')+1); //surfixing spaces
return str;
}
さらに別のオプション - 両端から1つ以上の文字を削除します。
string strip(const string& s, const string& chars=" ") {
size_t begin = 0;
size_t end = s.size()-1;
for(; begin < s.size(); begin++)
if(chars.find_first_of(s[begin]) == string::npos)
break;
for(; end > begin; end--)
if(chars.find_first_of(s[end]) == string::npos)
break;
return s.substr(begin, end-begin+1);
}
それを行うエレガントな方法は、次のようにすることができます
std::string & trim(std::string & str)
{
return ltrim(rtrim(str));
}
サポート機能は次のように実装されています。
std::string & ltrim(std::string & str)
{
auto it = std::find_if( str.begin() , str.end() , [](char ch){ return !std::isspace<char>(ch , std::locale::classic() ) ; } );
str.erase( str.begin() , it);
return str;
}
std::string & rtrim(std::string & str)
{
auto it = std::find_if( str.rbegin() , str.rend() , [](char ch){ return !std::isspace<char>(ch , std::locale::classic() ) ; } );
str.erase( it.base() , str.end() );
return str;
}
そしてこれらのすべてを整えたら、次のように書くこともできます:
std::string trim_copy(std::string const & str)
{
auto s = str;
return ltrim(rtrim(s));
}
トリムC ++ 11実装:
static void trim(std::string &s) {
s.erase(s.begin(), std::find_if_not(s.begin(), s.end(), [](char c){ return std::isspace(c); }));
s.erase(std::find_if_not(s.rbegin(), s.rend(), [](char c){ return std::isspace(c); }).base(), s.end());
}
上記の方法は素晴らしいですが、あなたのルーチンが空白とみなすもののために関数の組み合わせを使いたいことがあります。 この場合、ファンクタを使用して操作を結合すると面倒なことがあります。そのため、トリムのために修正できる単純なループが好きです。 ここにSOのCバージョンからコピーされたわずかに変更されたトリム関数があります。 この例では、英数字以外の文字をトリミングしています。
string trim(char const *str)
{
// Trim leading non-letters
while(!isalnum(*str)) str++;
// Trim trailing non-letters
end = str + strlen(str) - 1;
while(end > str && !isalnum(*end)) end--;
return string(str, end+1);
}
以前のC ++トリム関数をC ++ 11の手法で更新したいので、私はその質問に対する投稿された答えの多くをテストしました。 私の結論は、私は古いC ++ソリューションを保つことです!
チェックする文字をさらに追加しても、最も高速です(たとえば、\ r \ nは\ f \ vの使用例が見えません)。アルゴリズムを使用するソリューションよりもまだ高速です。
std::string & trimMe (std::string & str)
{
// right trim
while (str.length () > 0 && (str [str.length ()-1] == ' ' || str [str.length ()-1] == '\t'))
str.erase (str.length ()-1, 1);
// left trim
while (str.length () > 0 && (str [0] == ' ' || str [0] == '\t'))
str.erase (0, 1);
return str;
}
次のコードを使用して、 std::strings
( ideone )の空白とタブ文字を右トリム(後端)します。
// trim trailing spaces
size_t endpos = str.find_last_not_of(" \t");
size_t startpos = str.find_first_not_of(" \t");
if( std::string::npos != endpos )
{
str = str.substr( 0, endpos+1 );
str = str.substr( startpos );
}
else {
str.erase(std::remove(std::begin(str), std::end(str), ' '), std::end(str));
}
そしてバランスを取るために、左のトリムコードもideoneます( ideone ):
// trim leading spaces
size_t startpos = str.find_first_not_of(" \t");
if( string::npos != startpos )
{
str = str.substr( startpos );
}
私の答えは、制御文字と空白( ASCIIテーブルの 0-32と127)をトリムするこのポストの最上位の回答の改善です。
std::isgraph
はキャラクタがグラフィカル表現を持っているかどうかを判断するので、これを使ってエヴァンの答えを変更し、文字列のどちらかの側からグラフィカル表現を持たないキャラクタを削除することができます。 結果ははるかに洗練されたソリューションです:
#include <algorithm>
#include <functional>
#include <string>
/**
* @brief Left Trim
*
* Trims whitespace from the left end of the provided std::string
*
* @param[out] s The std::string to trim
*
* @return The modified std::string&
*/
std::string& ltrim(std::string& s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(),
std::ptr_fun<int, int>(std::isgraph)));
return s;
}
/**
* @brief Right Trim
*
* Trims whitespace from the right end of the provided std::string
*
* @param[out] s The std::string to trim
*
* @return The modified std::string&
*/
std::string& rtrim(std::string& s) {
s.erase(std::find_if(s.rbegin(), s.rend(),
std::ptr_fun<int, int>(std::isgraph)).base(), s.end());
return s;
}
/**
* @brief Trim
*
* Trims whitespace from both ends of the provided std::string
*
* @param[out] s The std::string to trim
*
* @return The modified std::string&
*/
std::string& trim(std::string& s) {
return ltrim(rtrim(s));
}
注意:ワイド文字のサポートが必要な場合は、 std::iswgraph
を使用できるはずですが、このコードを編集してstd::wstring
操作を有効にする必要があります。これはテストしていないものですこのオプションを調べるためのstd::basic_string
リファレンスページ)。
私はtzamanのソリューションが好きです。唯一の問題は、スペースだけを含む文字列をトリムしないことです。
その1つの欠陥を修正するには、2つのトリマー線の間にstr.clear()を追加します
std::stringstream trimmer;
trimmer << str;
str.clear();
trimmer >> str;
空文字列の場合、コードはstring::npos
1を加えると0をstring::npos
ますstring::npos
は、 string::size_type
の型で、符号なしです。 したがって、あなたは加算のオーバーフローの振る舞いに頼っています。