935

現在、次のコードを使用してstd::strings、プログラム内のすべてを右トリミングしています。

std::string s;
s.erase(s.find_last_not_of(" \n\r\t")+1);

正常に動作しますが、失敗する可能性のある最終的なケースがいくつかあるのだろうか?

もちろん、エレガントな代替案や左トリムのソリューションを使用した回答も大歓迎です。

4

48 に答える 48

769

編集c++17 以降、標準ライブラリの一部が削除されました。幸いなことに、c++11 以降では、優れたソリューションであるラムダがあります。

#include <algorithm> 
#include <cctype>
#include <locale>

// trim from start (in place)
static inline void ltrim(std::string &s) {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {
        return !std::isspace(ch);
    }));
}

// trim from end (in place)
static inline void rtrim(std::string &s) {
    s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {
        return !std::isspace(ch);
    }).base(), s.end());
}

// trim from both ends (in place)
static inline void trim(std::string &s) {
    ltrim(s);
    rtrim(s);
}

// trim from start (copying)
static inline std::string ltrim_copy(std::string s) {
    ltrim(s);
    return s;
}

// trim from end (copying)
static inline std::string rtrim_copy(std::string s) {
    rtrim(s);
    return s;
}

// trim from both ends (copying)
static inline std::string trim_copy(std::string s) {
    trim(s);
    return s;
}

最新のソリューションを提供してくれたhttps://stackoverflow.com/a/44973498/524503に感謝します。

元の答え:

トリミングのニーズに合わせて、次の 3 つのいずれかを使用する傾向があります。

#include <algorithm> 
#include <functional> 
#include <cctype>
#include <locale>

// trim from start
static inline std::string &ltrim(std::string &s) {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(),
            std::not1(std::ptr_fun<int, int>(std::isspace))));
    return s;
}

// trim from end
static inline std::string &rtrim(std::string &s) {
    s.erase(std::find_if(s.rbegin(), s.rend(),
            std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
    return s;
}

// trim from both ends
static inline std::string &trim(std::string &s) {
    return ltrim(rtrim(s));
}

それらはかなり自明であり、非常にうまく機能します。

編集:ところで、実際にはロケールをサポートする2番目の定義があるため、std::ptr_fun曖昧さを解消するためにそこにいます。std::isspaceこれはまったく同じキャストだったかもしれませんが、私はこちらの方が好きです。

EDIT : 参照によるパラメーターの受け入れ、変更、および戻りに関するいくつかのコメントに対処するには。同意します。私が好むと思われる実装は、2 セットの関数で、1 つはインプレース用で、もう 1 つはコピーを作成するものです。より良い例のセットは次のとおりです。

#include <algorithm> 
#include <functional> 
#include <cctype>
#include <locale>

// trim from start (in place)
static inline void ltrim(std::string &s) {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(),
            std::not1(std::ptr_fun<int, int>(std::isspace))));
}

// trim from end (in place)
static inline void rtrim(std::string &s) {
    s.erase(std::find_if(s.rbegin(), s.rend(),
            std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
}

// trim from both ends (in place)
static inline void trim(std::string &s) {
    ltrim(s);
    rtrim(s);
}

// trim from start (copying)
static inline std::string ltrim_copy(std::string s) {
    ltrim(s);
    return s;
}

// trim from end (copying)
static inline std::string rtrim_copy(std::string s) {
    rtrim(s);
    return s;
}

// trim from both ends (copying)
static inline std::string trim_copy(std::string s) {
    trim(s);
    return s;
}

ただし、コンテキストと高投票数の回答を引き続き利用できるようにするために、上記の元の回答を保持しています。

于 2008-10-20T05:46:38.777 に答える
447

Boostの文字列アルゴリズムを使用するのが最も簡単です。

#include <boost/algorithm/string.hpp>

std::string str("hello world! ");
boost::trim_right(str);

str"hello world!"です。両側をトリミングするともtrim_leftあります。trim


上記の関数名のいずれかに接尾辞を追加すると、_copyたとえばtrim_copy、関数は、参照を介して文字列を変更するのではなく、文字列のトリミングされたコピーを返します。

_if上記の関数名のいずれかに接尾辞を追加すると、たとえばtrim_copy_if、空白だけでなく、カスタム述語を満たすすべての文字をトリミングできます。

于 2008-10-19T19:55:06.820 に答える
77

あなたがやっていることは素晴らしく、堅牢です。私は長い間同じ方法を使用してきましたが、より高速な方法をまだ見つけていません。

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 (right then left)
inline std::string& trim(std::string& s, const char* t = ws)
{
    return ltrim(rtrim(s, t), t);
}

トリミングする文字を指定することで、空白以外の文字を柔軟にトリミングでき、トリミングしたい文字のみを効率的にトリミングできます。

于 2014-08-19T14:16:58.527 に答える
63

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 )も含めます。

// trim leading spaces
size_t startpos = str.find_first_not_of(" \t");
if( string::npos != startpos )
{
    str = str.substr( startpos );
}
于 2008-12-07T19:45:40.250 に答える
60

パーティーに少し遅れましたが、気にしないでください。C++11 が登場し、ラムダ変数と自動変数が用意されました。したがって、すべての空白と空の文字列も処理する私のバージョンは次のとおりです。

#include <cctype>
#include <string>
#include <algorithm>

inline std::string trim(const std::string &s)
{
   auto wsfront=std::find_if_not(s.begin(),s.end(),[](int c){return std::isspace(c);});
   auto wsback=std::find_if_not(s.rbegin(),s.rend(),[](int c){return std::isspace(c);}).base();
   return (wsback<=wsfront ? std::string() : std::string(wsfront,wsback));
}

から逆イテレータを作成し、wsfrontそれを 2 番目の終了条件として使用することもできますfind_if_notが、それはすべて空白文字列の場合にのみ有用であり、gcc 4.8 は少なくとも逆イテレータの型を推測するほどスマートではありません ( std::string::const_reverse_iterator) とauto。逆イテレータの構築にどれだけのコストがかかるかはわかりませんので、ここでは YMMV を使用します。この変更により、コードは次のようになります。

inline std::string trim(const std::string &s)
{
   auto  wsfront=std::find_if_not(s.begin(),s.end(),[](int c){return std::isspace(c);});
   return std::string(wsfront,std::find_if_not(s.rbegin(),std::string::const_reverse_iterator(wsfront),[](int c){return std::isspace(c);}).base());
}
于 2013-07-31T17:03:12.047 に答える
58

これを試してください、それは私のために働きます。

inline std::string trim(std::string& str)
{
    str.erase(str.find_last_not_of(' ')+1);         //suffixing spaces
    str.erase(0, str.find_first_not_of(' '));       //prefixing spaces
    return str;
}
于 2011-06-28T00:47:53.440 に答える
27

私は tzaman のソリューションが好きです。唯一の問題は、スペースのみを含む文字列をトリミングしないことです。

その 1 つの欠陥を修正するには、2 つのトリマー行の間に str.clear() を追加します。

std::stringstream trimmer;
trimmer << str;
str.clear();
trimmer >> str;
于 2010-07-05T06:37:20.050 に答える
27

http://ideone.com/nFVtEo

std::string trim(const std::string &s)
{
    std::string::const_iterator it = s.begin();
    while (it != s.end() && isspace(*it))
        it++;

    std::string::const_reverse_iterator rit = s.rbegin();
    while (rit.base() != it && isspace(*rit))
        rit++;

    return std::string(it, rit.base());
}
于 2014-02-11T10:15:06.107 に答える
19

空の文字列の場合、コードは に 1 を追加するとstring::npos0になるstring::nposと想定しますstring::size_type。したがって、加算のオーバーフロー動作に依存しています。

于 2008-10-19T19:26:43.850 に答える
13

Cplusplus.comのハッキング

std::string choppa(const std::string &t, const std::string &ws)
{
    std::string str = t;
    size_t found;
    found = str.find_last_not_of(ws);
    if (found != std::string::npos)
        str.erase(found+1);
    else
        str.clear();            // str is all whitespace

    return str;
}

これは、null の場合にも機能します。:-)

于 2008-10-19T20:47:47.057 に答える
12
s.erase(0, s.find_first_not_of(" \n\r\t"));                                                                                               
s.erase(s.find_last_not_of(" \n\r\t")+1);   
于 2015-10-13T10:02:16.440 に答える
11

@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);
}
于 2013-05-24T21:11:22.080 に答える
9

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));
}
于 2013-08-27T12:11:45.753 に答える
9

私の答えは、制御文字とスペース ( 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このオプションについては、のリファレンス ページを参照してください) 。 .

于 2015-03-21T17:14:29.993 に答える
8

それを行うエレガントな方法は次のようになります

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));
}
于 2015-08-18T11:21:39.923 に答える
7

正規表現を使用したトリムのソリューションは次のとおりです

#include <string>
#include <regex>

string trim(string str){
    return regex_replace(str, regex("(^[ ]+)|([ ]+$)"),"");
}
于 2019-11-08T19:59:33.370 に答える
6

文字列をトリミングする「最良の方法」を求め始めたら、次のような実装が良いと思います。

  1. 一時文字列を割り当てません
  2. インプレース トリムとコピー トリムのオーバーロードがあります
  3. さまざまな検証シーケンス/ロジックを受け入れるように簡単にカスタマイズできます

明らかに、これにアプローチするにはさまざまな方法があり、実際に必要なものによって異なります。ただし、C 標準ライブラリの <string.h> には、memchr などの非常に便利な関数がまだいくつかあります。C がまだ IO に最適な言語と見なされているのには理由があります。その stdlib は純粋に効率的です。

inline const char* trim_start(const char* str)
{
    while (memchr(" \t\n\r", *str, 4))  ++str;
    return str;
}
inline const char* trim_end(const char* end)
{
    while (memchr(" \t\n\r", end[-1], 4)) --end;
    return end;
}
inline std::string trim(const char* buffer, int len) // trim a buffer (input?)
{
    return std::string(trim_start(buffer), trim_end(buffer + len));
}
inline void trim_inplace(std::string& str)
{
    str.assign(trim_start(str.c_str()),
        trim_end(str.c_str() + str.length()));
}

int main()
{
    char str [] = "\t \nhello\r \t \n";

    string trimmed = trim(str, strlen(str));
    cout << "'" << trimmed << "'" << endl;

    system("pause");
    return 0;
}
于 2013-11-16T20:10:05.067 に答える
4

ノイズに対する私のソリューションに貢献します。trimデフォルトでは、新しい文字列を作成して変更された文字列を返し、trim_in_place渡された文字列を変更します。このtrim関数は、c++11 の移動セマンティクスをサポートしています。

#include <string>

// modifies input string, returns input

std::string& trim_left_in_place(std::string& str) {
    size_t i = 0;
    while(i < str.size() && isspace(str[i])) { ++i; };
    return str.erase(0, i);
}

std::string& trim_right_in_place(std::string& str) {
    size_t i = str.size();
    while(i > 0 && isspace(str[i - 1])) { --i; };
    return str.erase(i, str.size());
}

std::string& trim_in_place(std::string& str) {
    return trim_left_in_place(trim_right_in_place(str));
}

// returns newly created strings

std::string trim_right(std::string str) {
    return trim_right_in_place(str);
}

std::string trim_left(std::string str) {
    return trim_left_in_place(str);
}

std::string trim(std::string str) {
    return trim_left_in_place(trim_right_in_place(str));
}

#include <cassert>

int main() {

    std::string s1(" \t\r\n  ");
    std::string s2("  \r\nc");
    std::string s3("c \t");
    std::string s4("  \rc ");

    assert(trim(s1) == "");
    assert(trim(s2) == "c");
    assert(trim(s3) == "c");
    assert(trim(s4) == "c");

    assert(s1 == " \t\r\n  ");
    assert(s2 == "  \r\nc");
    assert(s3 == "c \t");
    assert(s4 == "  \rc ");

    assert(trim_in_place(s1) == "");
    assert(trim_in_place(s2) == "c");
    assert(trim_in_place(s3) == "c");
    assert(trim_in_place(s4) == "c");

    assert(s1 == "");
    assert(s2 == "c");
    assert(s3 == "c");
    assert(s4 == "c");  
}
于 2014-05-06T03:24:12.020 に答える
3

あなたの環境が同じかどうかはわかりませんが、私の場合、空の文字列の場合はプログラムが中止されます。その消去呼び出しを if(!s.empty()) でラップするか、既に述べたように Boost を使用します。

于 2008-10-19T20:03:35.197 に答える
3

これが私のバージョンです:

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);
于 2016-08-31T14:41:21.593 に答える
3

これは、どこにでも書くことに慣れておらず、正確性、s、STLなどにstd::まだ慣れていない初心者にとって理解しやすいソリューションです...constiteratoralgorithm

#include <string>
#include <cctype> // for isspace
using namespace std;


// Left trim the given string ("  hello!  " --> "hello!  ")
string left_trim(string str) {
    int numStartSpaces = 0;
    for (int i = 0; i < str.length(); i++) {
        if (!isspace(str[i])) break;
        numStartSpaces++;
    }
    return str.substr(numStartSpaces);
}

// Right trim the given string ("  hello!  " --> "  hello!")
string right_trim(string str) {
    int numEndSpaces = 0;
    for (int i = str.length() - 1; i >= 0; i--) {
        if (!isspace(str[i])) break;
        numEndSpaces++;
    }
    return str.substr(0, str.length() - numEndSpaces);
}

// Left and right trim the given string ("  hello!  " --> "hello!")
string trim(string str) {
    return right_trim(left_trim(str));
}

それが役に立てば幸い...

于 2016-12-08T12:14:19.880 に答える
3

これが私が思いついたものです:

std::stringstream trimmer;
trimmer << str;
trimmer >> str;

ストリーム抽出は空白を自動的に削除するため、これは魅力的に機能します。
私がそう言うなら、かなりきれいでエレガントでもあります。;)

于 2010-05-27T07:29:40.497 に答える
2

これは簡単な実装です。このような単純な操作の場合、おそらく特別な構造を使用するべきではありません。組み込みの isspace() 関数はさまざまな形式の白文字を処理するので、これを利用する必要があります。また、文字列が空であるか、単なるスペースの集まりである特殊なケースも考慮する必要があります。左または右のトリムは、次のコードから派生できます。

string trimSpace(const string &str) {
   if (str.empty()) return str;
   string::size_type i,j;
   i=0;
   while (i<str.size() && isspace(str[i])) ++i;
   if (i == str.size())
      return string(); // empty string
   j = str.size() - 1;
   //while (j>0 && isspace(str[j])) --j; // the j>0 check is not needed
   while (isspace(str[j])) --j
   return str.substr(i, j-i+1);
}
于 2016-03-23T04:40:48.870 に答える
2

上記のメソッドは優れていますが、ルーチンが空白と見なすものに対して関数の組み合わせを使用したい場合があります。この場合、ファンクターを使用して操作を組み合わせると面倒になる可能性があるため、トリム用に変更できる単純なループを好みます。これは、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);
}
于 2009-11-24T19:26:07.967 に答える
2

これはどうですか...?

#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++ に比較的慣れていないので、ここから外れている場合はご容赦ください。

于 2013-10-03T19:32:58.033 に答える
1

さらに別のオプション - 両端から 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);
}
于 2013-03-27T00:19:48.493 に答える
1

このバージョンは、内部の空白と英数字以外を削除します:

static inline std::string &trimAll(std::string &s)
{   
    if(s.size() == 0)
    {
        return s;
    }

    int val = 0;
    for (int cur = 0; cur < s.size(); cur++)
    {
        if(s[cur] != ' ' && std::isalnum(s[cur]))
        {
            s[val] = s[cur];
            val++;
        }
    }
    s.resize(val);
    return s;
}
于 2010-08-24T17:57:50.290 に答える
0
std::string trim( std::string && str )
{
    size_t end = str.find_last_not_of( " \n\r\t" );
    if ( end != std::string::npos )
        str.resize( end + 1 );

    size_t start = str.find_first_not_of( " \n\r\t" );
    if ( start != std::string::npos )
        str = str.substr( start );

    return std::move( str );
}
于 2013-11-28T09:57:33.880 に答える
0

以下は、1 パス (2 パスの場合もある) ソリューションです。文字列の空白部分を 2 回、非空白部分を 1 回通過します。

void trim(std::string& s) {                                                                                                                                                                                                               
    if (s.empty())                                                                                                                                                                                                                        
        return;                                                                                                                                                                                                                           

    int l = 0, r = s.size()  - 1;                                                                                                                                                                                                         

    while (l < s.size() && std::isspace(s[l++])); // l points to first non-whitespace char.                                                                                                                                               
    while (r >= 0 && std::isspace(s[r--])); // r points to last non-whitespace char.                                                                                                                                                      

    if (l > r)                                                                                                                                                                                                                            
        s = "";                                                                                                                                                                                                                           
    else {                                                                                                                                                                                                                                
        l--;                                                                                                                                                                                                                              
        r++;                                                                                                                                                                                                                              
        int wi = 0;                                                                                                                                                                                                                       
        while (l <= r)                                                                                                                                                                                                                    
            s[wi++] = s[l++];                                                                                                                                                                                                             
        s.erase(wi);                                                                                                                                                                                                                      
    }                                                                                                                                                                                                                                     
    return;                                                                                                                                                                                                                               
}                                          
于 2018-09-26T19:31:33.327 に答える
0

両端をトリミングします。

string trim(const std::string &str){
    string result = "";
    size_t endIndex = str.size();
    while (endIndex > 0 && isblank(str[endIndex-1]))
        endIndex -= 1;
    for (size_t i=0; i<endIndex ; i+=1){
        char ch = str[i];
        if (!isblank(ch) || result.size()>0)
            result += ch;
    }
   return result;
}
于 2020-08-19T22:11:28.813 に答える