std :: stringをトリムする最良の方法は何ですか?


812

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

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

それは正常に動作しますが、失敗する可能性のあるいくつかのエンドケースはあるのでしょうか?

もちろん、エレガントな選択肢と左トリムソリューションによる回答も歓迎します。


549
この質問への回答は、C ++標準ライブラリがいかに不足しているかを証明するものです。
Idan K 2010

83
@IdanKそしてC ++ 11ではまだこの関数はありません。
量子

44
@IdanK:すばらしいですね。私たちは今「の一人のアイデアによって邪魔されない、私たちの処分で持っているすべての競合のオプションを見て、我々はそれを行う必要があることを道」!
オービットのライトネスレース2013年

59
タイプ内の@LightnessRacesinOrbit機能、それは設計上の決定であり、文字列にトリム関数を追加することは(少なくともC ++では)とにかく最善の解決策ではないかもしれません-しかし、それを行うための標準的な方法を提供せず、代わりに全員に悩ませる何度も何度も同じような小さな問題は、確かにどちらかの人を助けていません
codeling

27
std::string他の言語を使いやすくするような関数(Pythonなど)である場合、トリミング関数がクラスに組み込まれていない理由を疑問に思うかもしれません。
HelloGoodbye 2014年

回答:


648

編集 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(), [](int 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(), [](int 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つの1つを使用する傾向があります。

#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ためにそこにいます。これはまったく同じキャストだったかもしれませんが、私はこれを好む傾向があります。

編集:参照によるパラメーターの受け入れ、変更、返却に関するコメントに対処するため。同意する。私が好むと思われる実装は、機能の2つのセットです。より良い例のセットは次のとおりです。

#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;
}

私は上記の元の回答を保持していますが、状況と、投票率の高い回答を引き続き利用できるようにするためです。


28
このコードは、一部の国際文字列(私の場合、shift-jis、std :: stringに格納されている)で失敗しました。私boost::trimは問題を解決するために使用することになりました。
トム

5
参照の代わりにポインタを使用します。これにより、コールポイントから、これらの関数がコピーを作成する代わりに文字列を編集することを理解しやすくなります。
Marco Leogrande 2012

3
isspaceであなたは簡単に非ASCII文字で未定義の動作を得ることができることに注意してくださいstacked-crooked.com/view?id=49bf8b0759f0dd36dffdad47663ac69f
R.マルティーニ・フェルナンデス

10
なぜ静的なのですか?これは匿名の名前空間が望ましい場所ですか?
Trevor Hickey 2013

3
@TrevorHickeyの場合は、代わりに匿名の名前空間を使用することもできます。
エヴァンテラン2013

417

使用してブーストの文字列アルゴリズムは、最も簡単になります:

#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、空白だけではなく、カスタム述語を満たすすべての文字をトリミングできます。


7
それはロケールに依存します。デフォルトのロケール(VS2005、en)は、タブ、スペース、キャリッジリターン、改行、垂直タブ、およびフォームフィードがトリミングされることを意味します。
MattyT 2009年

117
ブーストは、このような小さな問題のための非常に大きなハンマーです。
Casey Rodarmor

143
@rodarmor:Boostは多くの小さな問題を解決します。それは多くを解決する巨大なハンマーです。
Nicol Bolas、2012年

123
ブーストは、さまざまな問題を解決するさまざまなサイズのハンマーのセットです。
イブラヒム

11
@rodarmorあなたは、Boostが全か無かのモノリスであるかのように言っています。ヘッダーの1つをインクルードすると、何らかの形でプログラム全体に影響を与えます。明らかにそうではありません。ところで、私はBoostを使用したことがありません、fwiw。
underscore_d

61

次のコードを使用して、std::stringsideone)からスペースとタブ文字を右トリム(末尾)します。

// 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 );
}

4
これは他の形式の空白を検出しません...特に改行、ラインフィード、キャリッジリターン。
トム・

1
正しい。トリミングしたい空白に合わせてカスタマイズする必要があります。私の特定のアプリケーションでは、スペースとタブのみを想定していましたが、\ n \ rを追加して他のものをキャッチできます。
リザードに請求する

5
str.substr(...).swap(str)優れている。割り当てを保存します。
updogliu 2012

4
@updogliu移動割り当てを使用しませんbasic_string& operator= (basic_string&& str) noexcept;か?
nurettin 2013年

8
この回答は、すべてスペースである文字列を変更しません。それは失敗です。
トムアンデルセン

56

あなたがやっていることは、細かくて頑丈です。私は同じ方法を長い間使用しており、まだより高速な方法を見つけていません。

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);
}

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


で順序を変更する場合trim、つまり、順序をrtrim(ltrim(s, t), t)少し効率化する場合
CITBL

1
@CITBL内部関数が最初に実行されるため、右からトリミングするに左からトリミングします。私はそれが可能だと思うあまり、それはないだろう効率的ですか?
Galik

丁度。私の間違い
CITBL

CharTでbasic_stringとテンプレートを使用する場合は、すべての文字列に対してこれを行うことができます。空白にはテンプレート変数を使用して、ws <CharT>のように使用します。技術的にはその時点であなたは、C ++ 20のための準備が完了しますと、これはインライン暗示するように、それはあまりにもconstexprのマークでした
海岸に

@Beached確かに。ここで答えを入れるのは少し複雑です。私はこれのためのテンプレート関数を書いており、確かにかなり複雑です。私はさまざまなアプローチを試しましたが、どれが最善かわかりません。
Galik

55

パーティーに少し遅れますが、気にしないでください。これで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());
}

9
いいね。私からの+1。悪いC ++ 11はstd :: stringにtrim()を導入せず、誰にとっても楽になりました。
ミラノバブスコフ2013

3
文字列を実装するのではなく、常に1回の関数呼び出しでトリミングしたい
linquize

22
それが価値があるために、そのラムダを使用する必要はありません。単に渡すことができますstd::isspaceauto wsfront=std::find_if_not(s.begin(),s.end(),std::isspace);
vmrob

4
+1は、おそらくO(N)文字列のコピーを1つだけ実行する実装の唯一の回答です。
Alexei Averchenko 2014

4
@vmrobコンパイラは、必ずしもそれほどスマートではありません。あなたが言うことをすることは曖昧です:candidate template ignored: couldn't infer template argument '_Predicate' find_if_not(_InputIterator __first, _InputIterator __last, _Predicate __pred)
johnbakers

42

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

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;
}

12
文字列にサフィックススペースが含まれていない場合、npos + 1 == 0から消去され、文字列全体が削除されます。
mhsmith 2012年

3
@rgove説明してください。str.find_last_not_of(x)xと等しくない最初の文字の位置を返します。xと一致しない文字がない場合にのみ、nposを返します。この例では、接尾辞のスペースがない場合、と同等の結果が返され、str.length() - 1基本的にstr.erase((str.length() - 1) + 1).は次のようになります。
Travis

5
これは、コピーコンストラクタを不必要に呼び出さないようにstd :: string&を返す必要があります。
heksesang 2014年

7
戻りパラメーターを変更した後、なぜこれがコピーを返すのか混乱していますか?
Galik 2015年

3
@MiloDC私の混乱は、参照なくコピー返す理由です。戻るほうが理にかなっていますstd::string&
Galik

25

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

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

std::stringstream trimmer;
trimmer << str;
str.clear();
trimmer >> str;

いいですね:)両方のソリューションの問題は、両端がトリミングされることです。作ることができないltrimrtrimのように。
tzaman 2010

44
良いですが、内部に空白がある文字列は処理できません。例:trim(abc def ")-> abc、abcのみ

内部の空白がないことがわかっている場合は、良い解決策です!
Elliot Gorokhovsky

これは素晴らしくて簡単ですが、文字列がにコピーされたりからコピーされたりするので、非常に遅くなりますstd::stringstream
Galik

23

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());
}

1
ついにベーシックスペーストリムのエレガントなソリューション... :)
jave.web

これがどのように機能するか:これはコピーのようなソリューションです-スペース(it)ではない最初の文字の位置を検出し、逆:スペース(rit)のみがある文字の位置-その後、新しく作成された文字列==を返します元の文字列の一部のコピー-これらのイテレータに基づく一部...
jave.web '26

ありがとう、私のために働きました:std:string s = "ああnoez:スペース\ r \ n"; std :: string clean = trim(s);
Alexx Roche

15

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


23
あなたはそれが悪いかのように言い回している。符号付き整数オーバーフローの動作が不正です。
MSalters 2008年

2
追加1するには、std::string::npos しなければならない与える0によるとC++ Standard。したがって、これは完全に信頼できる適切な仮定です。
Galik 2018

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の場合にも機能します。:-)


4
これは単なるrtrim、ではなくltrim
ub3rst4r 2014年

1
^ find_first_not_ofを使用してもよろしいですか?変更は比較的簡単です。
Abhinav Gauniyal

13

C ++ 17では、basic_string_view :: remove_prefixbasic_string_view :: remove_suffixを使用できます。

std::string_view trim(std::string_view s)
{
    s.remove_prefix(std::min(s.find_first_not_of(" \t\r\v\n"), s.size()));
    s.remove_suffix(std::min(s.size() - s.find_last_not_of(" \t\r\v\n") - 1, s.size()));

    return s;
}

素晴らしい代替手段:

std::string_view ltrim(std::string_view s)
{
    s.remove_prefix(std::distance(s.cbegin(), std::find_if(s.cbegin(), s.cend(),
         [](int c) {return !std::isspace(c);})));

    return s;
}

std::string_view rtrim(std::string_view s)
{
    s.remove_suffix(std::distance(s.crbegin(), std::find_if(s.crbegin(), s.crend(),
        [](int c) {return !std::isspace(c);})));

    return s;
}

std::string_view trim(std::string_view s)
{
    return ltrim(rtrim(s));
}

何をテストしているのかわかりませんが、例ではstd :: find_first_not_ofstd :: string :: nposを返しstd :: string_view :: sizeは4を返します。最小値は明らかに4で、要素の数はstd :: string_view :: remove_prefixによって削除されました。gcc 9.2とclang 9.0はどちらもこれを正しく処理します:godbolt.org/z/DcZbFH
Phidelux

1
ありがとう!は、私にはよく見えますよ。
Contango

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);
}

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ます。 。


3
std :: ptr_funは非推奨
johnbakers

8

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));
}

8

これは私が使用するものです。前面からスペースを取り除き、残っているものがあれば、背面から同じようにします。

void trim(string& s) {
    while(s.compare(0,1," ")==0)
        s.erase(s.begin()); // remove leading whitespaces
    while(s.size()>0 && s.compare(s.size()-1,1," ")==0)
        s.erase(s.end()-1); // remove trailing whitespaces
}

8
s.erase(0, s.find_first_not_of(" \n\r\t"));                                                                                               
s.erase(s.find_last_not_of(" \n\r\t")+1);   

2
反対の順序でそれらを行い、最初に右からトリミングしてから、左をトリミングしてシフトを呼び出すと、少し効率が良くなります。
Galik 2018

7

価値があることについては、パフォーマンスに目を向けたトリム実装があります。これは、私が見た他の多くのトリムルーチンよりもはるかに高速です。イテレータとstd :: findsを使用する代わりに、生のc文字列とインデックスを使用します。次の特殊なケースを最適化します:サイズ0の文字列(何もしない)、トリムする空白がない文字列(何もしない)、トリムする末尾の空白のみがある文字列(文字列のサイズを変更する)、完全に空白文字列(文字列をクリアする) 。最後に、最悪の場合(先頭に空白がある文字列)、効率的なコピー構築を実行し、1つのコピーのみを実行してから、元の文字列の代わりにそのコピーを移動するのが最善です。

void TrimString(std::string & str)
{ 
    if(str.empty())
        return;

    const auto pStr = str.c_str();

    size_t front = 0;
    while(front < str.length() && std::isspace(int(pStr[front]))) {++front;}

    size_t back = str.length();
    while(back > front && std::isspace(int(pStr[back-1]))) {--back;}

    if(0 == front)
    {
        if(back < str.length())
        {
            str.resize(back - front);
        }
    }
    else if(back <= front)
    {
        str.clear();
    }
    else
    {
        str = std::move(std::string(str.begin()+front, str.begin()+back));
    }
}

@bmgdaおそらく理論的には最速のバージョンがこのシグネチャを持っている可能性があります:extern "C" void string_trim(char ** begin_、char ** end_)...ドリフトをキャッチしますか?

6

それを行うエレガントな方法は

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));
}

6

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());
}

5

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

  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;
}

3

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


3

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

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

ストリーム抽出は空白を自動的に削除するので、これは魅力のように機能します。
自分でそう言っても、とても清潔でエレガントです。;)


15
うーん; これは、文字列に内部空白(スペースなど)がないことを前提としています。OPは、左側または右側の空白を削除したいとだけ述べました。
SuperElectric 2010年

3

私の解決策をノイズに貢献しています。trimデフォルトでは、新しい文字列を作成し、trim_in_place渡された文字列を変更しながら変更された文字列を返します。trim++ 11の移動セマンティクスC関数をサポート。

#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");  
}

3

これは、back()およびの追加により、C ++ 11ではより簡単に実行できますpop_back()

while ( !s.empty() && isspace(s.back()) ) s.pop_back();

OPによって提案されたアプローチも悪くはありません-追跡するのが少し難しいだけです。
nobar 2016年

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);

最後の文字がありません。長さの+1はこれを解決します
ガリネット

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);
}

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);
}

2

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

#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));
}

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


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;
}

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);
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.