文字列から先頭と末尾のスペースを削除する


91

C ++で文字列オブジェクトからスペースを削除する方法。
たとえば、下の文字列オブジェクトから先頭と末尾のスペースを削除する方法。

//Original string: "         This is a sample string                    "
//Desired string: "This is a sample string"

文字列クラスは、私が知る限り、先頭と末尾のスペースを削除するメソッドを提供していません。

問題を追加するには、このフォーマットを拡張して、文字列の単語間の余分なスペースを処理する方法。例えば、

// Original string: "          This       is         a sample   string    " 
// Desired string:  "This is a sample string"  

ソリューションで説明されている文字列メソッドを使用すると、これらの操作を2つのステップで実行することが考えられます。

  1. 先頭と末尾のスペースを削除します。
  2. 使用find_last_of、find_first_not_of、find_last_not_ofとSUBSTR、find_first_of、繰り返し単語の境界でフォーマットしたい取得します。

回答:


127

これはトリミングと呼ばれます。Boostが使えるなら、お勧めです。

それ以外の場合は、を使用find_first_not_ofして最初の非空白文字find_last_not_ofのインデックスを取得してから、空白ではない末尾からインデックスを取得します。これらを使用substrして、周囲に空白のない部分文字列を取得するために使用します。

あなたの編集に応えて、私はその用語を知りませんが、「減らす」という線に沿って何かを推測するので、それを私はそれと呼んだのです。:)(注:柔軟性のために、空白をパラメーターに変更しました)

#include <iostream>
#include <string>

std::string trim(const std::string& str,
                 const std::string& whitespace = " \t")
{
    const auto strBegin = str.find_first_not_of(whitespace);
    if (strBegin == std::string::npos)
        return ""; // no content

    const auto strEnd = str.find_last_not_of(whitespace);
    const auto strRange = strEnd - strBegin + 1;

    return str.substr(strBegin, strRange);
}

std::string reduce(const std::string& str,
                   const std::string& fill = " ",
                   const std::string& whitespace = " \t")
{
    // trim first
    auto result = trim(str, whitespace);

    // replace sub ranges
    auto beginSpace = result.find_first_of(whitespace);
    while (beginSpace != std::string::npos)
    {
        const auto endSpace = result.find_first_not_of(whitespace, beginSpace);
        const auto range = endSpace - beginSpace;

        result.replace(beginSpace, range, fill);

        const auto newStart = beginSpace + fill.length();
        beginSpace = result.find_first_of(whitespace, newStart);
    }

    return result;
}

int main(void)
{
    const std::string foo = "    too much\t   \tspace\t\t\t  ";
    const std::string bar = "one\ntwo";

    std::cout << "[" << trim(foo) << "]" << std::endl;
    std::cout << "[" << reduce(foo) << "]" << std::endl;
    std::cout << "[" << reduce(foo, "-") << "]" << std::endl;

    std::cout << "[" << trim(bar) << "]" << std::endl;
}

結果:

[too much               space]  
[too much space]  
[too-much-space]  
[one  
two]  

私はあなたが「size_t」を意味したと思います。部分文字列が1つずつずれているので、substr(beginStr、endStr --beginStr + 1);にする必要があります。
goldPseudo 2009年

する必要site_tがありsize_tますか?そして、私はあなたがコメントを持っているところだと思いますno whitespaceは、文字列がすべて空白または空意味する。
フレッドラーソン

おかげで、修正しました size_tで、編集タイプミスとオフバイワンをが、私のコメントが反転していることに気づきませんでした、ありがとう。
GManNickG 2009年

@GManは非常にエレガントなソリューションです。ありがとう。
Ankur

バグ:trim()を介して「one \ ttwo」を実行してみてください。結果は空の文字列です。また、std :: string :: nposに対してendStrをテストする必要があります。
dlchambers 2012年

48

std :: stringから先頭、末尾、余分なスペースを1行で簡単に削除できます

value = std::regex_replace(value, std::regex("^ +| +$|( ) +"), "$1");

先頭のスペースのみを削除する

value.erase(value.begin(), std::find_if(value.begin(), value.end(), std::bind1st(std::not_equal_to<char>(), ' ')));

または

value = std::regex_replace(value, std::regex("^ +"), "");

末尾のスペースのみを削除する

value.erase(std::find_if(value.rbegin(), value.rend(), std::bind1st(std::not_equal_to<char>(), ' ')).base(), value.end());

または

value = std::regex_replace(value, std::regex(" +$"), "");

余分なスペースのみを削除する

value = regex_replace(value, std::regex(" +"), " ");

3
良いですね。これらのコードを理解するのは難しいので、ここで何が起こっているかについての情報を提供することは有用でしょう。
Marcin 2014年

ただし、C ++ 11でのみ機能します。
マーティンペッカ2015

7
タブは削除されませんが、修正できます。修正できないのは、それがひどく遅いことです(substrまたはで答えるよりも約100倍遅いerase)。
4LegsDrivenCat 2015年

速度最適化の場合、正規表現は最適なソリューションではありませんが、正規表現のインスタンスを1回作成することで改善できます
Evgeny Karpov 2015年

40

私は現在これらの関数を使用しています:

// trim from left
inline std::string& ltrim(std::string& s, const char* t = " \t\n\r\f\v")
{
    s.erase(0, s.find_first_not_of(t));
    return s;
}

// trim from right
inline std::string& rtrim(std::string& s, const char* t = " \t\n\r\f\v")
{
    s.erase(s.find_last_not_of(t) + 1);
    return s;
}

// trim from left & right
inline std::string& trim(std::string& s, const char* t = " \t\n\r\f\v")
{
    return ltrim(rtrim(s, t), t);
}

// copying versions

inline std::string ltrim_copy(std::string s, const char* t = " \t\n\r\f\v")
{
    return ltrim(s, t);
}

inline std::string rtrim_copy(std::string s, const char* t = " \t\n\r\f\v")
{
    return rtrim(s, t);
}

inline std::string trim_copy(std::string s, const char* t = " \t\n\r\f\v")
{
    return trim(s, t);
}


9

これは、先頭と末尾のスペースを削除するための私のソリューションです...

std::string stripString = "  Plamen     ";
while(!stripString.empty() && std::isspace(*stripString.begin()))
    stripString.erase(stripString.begin());

while(!stripString.empty() && std::isspace(*stripString.rbegin()))
    stripString.erase(stripString.length()-1);

結果は「プラメン」です


8

これがあなたがそれをすることができる方法です:

std::string & trim(std::string & str)
{
   return ltrim(rtrim(str));
}

そして、支援機能は次のように実装されます。

std::string & ltrim(std::string & str)
{
  auto it2 =  std::find_if( str.begin() , str.end() , [](char ch){ return !std::isspace<char>(ch , std::locale::classic() ) ; } );
  str.erase( str.begin() , it2);
  return str;   
}

std::string & rtrim(std::string & str)
{
  auto it1 =  std::find_if( str.rbegin() , str.rend() , [](char ch){ return !std::isspace<char>(ch , std::locale::classic() ) ; } );
  str.erase( it1.base() , str.end() );
  return str;   
}

そして、これらすべてを配置したら、これも書くことができます:

std::string trim_copy(std::string const & str)
{
   auto s = str;
   return ltrim(rtrim(s));
}

これを試して


7

ブーストを使用するというジョンハンソンの提案に従って、先頭と末尾のスペースをトリムする例(末尾と保留中のスペースのみを削除します):

#include <boost/algorithm/string/trim.hpp>

std::string str = "   t e s t    ";

boost::algorithm::trim ( str );

結果は "t e s t"

もあります

  • trim_left 結果は "t e s t "
  • trim_right 結果は " t e s t"

5
/// strip a string, remove leading and trailing spaces
void strip(const string& in, string& out)
{
    string::const_iterator b = in.begin(), e = in.end();

    // skipping leading spaces
    while (isSpace(*b)){
        ++b;
    }

    if (b != e){
        // skipping trailing spaces
        while (isSpace(*(e-1))){
            --e;
        }
    }

    out.assign(b, e);
}

上記のコードでは、isSpace()関数は、文字が空白かどうかを示すブール関数です。この関数を実装してニーズを反映するか、必要に応じて「ctype.h」からisspace()を呼び出すことができます。 。


4

先頭と末尾のスペースをトリミングする例

std::string aString("    This is a string to be trimmed   ");
auto start = aString.find_first_not_of(' ');
auto end = aString.find_last_not_of(' ');
std::string trimmedString;
trimmedString = aString.substr(start, (end - start) + 1);

または

trimmedSring = aString.substr(aString.find_first_not_of(' '), (aString.find_last_not_of(' ') - aString.find_first_not_of(' ')) + 1);

3
人々は、文字列をトリミングする方法を学ぶために10ページのコードを調べることを好まないでしょう。
Thinkal VB 2017

2
文字列にスペースしかない場合は壊れます
DAG 2018

3

標準ライブラリを使用することには多くの利点がありますが、例外を引き起こすいくつかの特殊なケースに注意する必要があります。たとえば、C ++文字列にUnicode文字が含まれている場合の回答はありませんでした。この場合、関数isspaceを使用するとと、例外がスローされます。

私は次のコードを使用して、文字列のトリミングやその他の便利な操作を行っています。このコードの主な利点は次のとおりです。非常に高速で(これまでにテストしたどのコードよりも高速)、標準ライブラリのみを使用し、例外が発生することはありません。

#include <string>
#include <algorithm>
#include <functional>
#include <locale>
#include <iostream>

typedef unsigned char BYTE;

std::string strTrim(std::string s, char option = 0)
{
    // convert all whitespace characters to a standard space
    std::replace_if(s.begin(), s.end(), (std::function<int(BYTE)>)::isspace, ' ');

    // remove leading and trailing spaces
    size_t f = s.find_first_not_of(' ');
    if (f == std::string::npos) return "";
    s = s.substr(f, s.find_last_not_of(' ') - f + 1);

    // remove consecutive spaces
    s = std::string(s.begin(), std::unique(s.begin(), s.end(),
        [](BYTE l, BYTE r){ return l == ' ' && r == ' '; }));

    switch (option)
    {
    case 'l':  // convert to lowercase
        std::transform(s.begin(), s.end(), s.begin(), ::tolower);
        return s;
    case 'U':  // convert to uppercase
        std::transform(s.begin(), s.end(), s.begin(), ::toupper);
        return s;
    case 'n':  // remove all spaces
        s.erase(std::remove(s.begin(), s.end(), ' '), s.end());
        return s;
    default: // just trim
        return s;
    }
}

3

これはすべての中で最も単純かもしれません。

とを使用string::findstring::rfindて、両側から空白を見つけ、文字列を減らすことができます。

void TrimWord(std::string& word)
{
    if (word.empty()) return;

    // Trim spaces from left side
    while (word.find(" ") == 0)
    {
        word.erase(0, 1);
    }

    // Trim spaces from right side
    size_t len = word.size();
    while (word.rfind(" ") == --len)
    {
        word.erase(len, len + 1);
    }
}

2

私はこれをテストしました、それはすべて動作します。したがって、このメソッドprocessInputは、ユーザーに何かを入力するように要求するだけです。内部に余分なスペースがなく、最初または最後に余分なスペースがない文字列を返します。お役に立てれば。(また、理解しやすいようにコメントの山を入れてください)。

下部のmain()でそれを実装する方法を見ることができます

#include <string>
#include <iostream>

string processInput() {
  char inputChar[256];
  string output = "";
  int outputLength = 0;
  bool space = false;
  // user inputs a string.. well a char array
  cin.getline(inputChar,256);
  output = inputChar;
       string outputToLower = "";
  // put characters to lower and reduce spaces
  for(int i = 0; i < output.length(); i++){
    // if it's caps put it to lowercase
    output[i] = tolower(output[i]);
    // make sure we do not include tabs or line returns or weird symbol for null entry array thingy
    if (output[i] != '\t' && output[i] != '\n' && output[i] != 'Ì') {
      if (space) {
        // if the previous space was a space but this one is not, then space now is false and add char
        if (output[i] != ' ') {
          space = false;
          // add the char
          outputToLower+=output[i];
        }
      } else {
        // if space is false, make it true if the char is a space
        if (output[i] == ' ') {
          space = true;
        }
        // add the char
        outputToLower+=output[i];
      }
    }
  }
  // trim leading and tailing space
  string trimmedOutput = "";
  for(int i = 0; i < outputToLower.length(); i++){
    // if it's the last character and it's not a space, then add it
    // if it's the first character and it's not a space, then add it
    // if it's not the first or the last then add it
    if (i == outputToLower.length() - 1 && outputToLower[i] != ' ' || 
      i == 0 && outputToLower[i] != ' ' || 
      i > 0 && i < outputToLower.length() - 1) {
      trimmedOutput += outputToLower[i];
    } 
  }
  // return
  output = trimmedOutput;
  return output;
}

int main() {
  cout << "Username: ";
  string userName = processInput();
  cout << "\nModified Input = " << userName << endl;
}

2

なぜ複雑なのですか?

std::string removeSpaces(std::string x){
    if(x[0] == ' ') { x.erase(0, 1); return removeSpaces(x); }
    if(x[x.length() - 1] == ' ') { x.erase(x.length() - 1, x.length()); return removeSpaces(x); }
    else return x;
}

これは、ブーストが失敗した場合でも、正規表現、奇妙なもの、ライブラリがない場合でも機能します。

編集:MMのコメントの修正。


これは、空白の長さを計算し、両端に1回の消去呼び出しを使用する場合と比較すると、やや非効率的です
MM

1

C ++ 17が導入されました。これstd::basic_string_viewは、charのようなオブジェクトの一定の連続したシーケンス、つまり文字列のビューを参照するクラステンプレートです。と非常によく似たインターフェースを持つことにstd::basic_string加えて、2つの追加機能がありますremove_prefix()。これは、開始を前方に移動することによってビューを縮小します。および remove_suffix()、その端を後方に移動することによってビューを縮小します。これらは、先頭と末尾のスペースをトリミングするために使用できます。

#include <string_view>
#include <string>

std::string_view ltrim(std::string_view str)
{
    const auto pos(str.find_first_not_of(" \t"));
    str.remove_prefix(pos);
    return str;
}

std::string_view rtrim(std::string_view str)
{
    const auto pos(str.find_last_not_of(" \t"));
    str.remove_suffix(str.length() - pos - 1);
    return str;
}

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

int main()
{
    std::string str = "   hello world   ";
    auto sv1{ ltrim(str) };  // "hello world   "
    auto sv2{ rtrim(str) };  // "   hello world"
    auto sv3{ trim(str) };   // "hello world"

    //If you want, you can create std::string objects from std::string_view objects
    auto s1{ sv1 };
    auto s2{ sv2 };
    auto s3{ sv3 };
}

注:はstd::string_view所有権のない参照であるため、元の文字列がまだ存在している場合にのみ有効です。


0
    char *str = (char*) malloc(50 * sizeof(char));
    strcpy(str, "    some random string (<50 chars)  ");

    while(*str == ' ' || *str == '\t' || *str == '\n')
            str++;

    int len = strlen(str);

    while(len >= 0 && 
            (str[len - 1] == ' ' || str[len - 1] == '\t' || *str == '\n')
    {
            *(str + len - 1) = '\0';
            len--;
    }

    printf(":%s:\n", str);

0
void removeSpaces(string& str)
{
    /* remove multiple spaces */
    int k=0;
    for (int j=0; j<str.size(); ++j)
    {
            if ( (str[j] != ' ') || (str[j] == ' ' && str[j+1] != ' ' ))
            {
                    str [k] = str [j];
                    ++k;
            }

    }
    str.resize(k);

    /* remove space at the end */   
    if (str [k-1] == ' ')
            str.erase(str.end()-1);
    /* remove space at the begin */
    if (str [0] == ' ')
            str.erase(str.begin());
}

0
string trim(const string & sStr)
{
    int nSize = sStr.size();
    int nSPos = 0, nEPos = 1, i;
    for(i = 0; i< nSize; ++i) {
        if( !isspace( sStr[i] ) ) {
            nSPos = i ;
            break;
        }
    }
    for(i = nSize -1 ; i >= 0 ; --i) {
        if( !isspace( sStr[i] ) ) {
            nEPos = i;
            break;
        }
    }
    return string(sStr, nSPos, nEPos - nSPos + 1);
}

0

先頭と末尾のスペースについては、次のようにします。

string string_trim(const string& in) {

    stringstream ss;
    string out;
    ss << in;
    ss >> out;
    return out;

}

または文の場合:

string trim_words(const string& sentence) {
    stringstream ss;
    ss << sentence;
    string s;
    string out;

    while(ss >> s) {

        out+=(s+' ');
    }
    return out.substr(0, out.length()-1);
}

0

清楚な

 void trimLeftTrailingSpaces(string &input) {
        input.erase(input.begin(), find_if(input.begin(), input.end(), [](int ch) {
            return !isspace(ch);
        }));
    }

    void trimRightTrailingSpaces(string &input) {
        input.erase(find_if(input.rbegin(), input.rend(), [](int ch) {
            return !isspace(ch);
        }).base(), input.end());
    }

0

いいえboost、いいえ、ライブラリregexだけstringです。とても簡単です。

string trim(const string s) { // removes whitespace characters from beginnig and end of string s
    const int l = (int)s.length();
    int a=0, b=l-1;
    char c;
    while(a<l && ((c=s.at(a))==' '||c=='\t'||c=='\n'||c=='\v'||c=='\f'||c=='\r'||c=='\0')) a++;
    while(b>a && ((c=s.at(b))==' '||c=='\t'||c=='\n'||c=='\v'||c=='\f'||c=='\r'||c=='\0')) b--;
    return s.substr(a, 1+b-a);
}

1
...そして、2Mのヘッダーファイルをビルドに取り込むことを避けました!
larry_C

0

問題を追加するには、このフォーマットを拡張して、文字列の単語間の余分なスペースを処理する方法。

実際、これは、複数の先頭と末尾の空白文字を考慮するよりも単純なケースです。あなたがする必要があるのは、文字列全体から重複する隣接する空白文字を削除することです。

隣接する空白の述語は単純に次のようになります。

auto by_space = [](unsigned char a, unsigned char b) {
    return std::isspace(a) and std::isspace(b);
};

次に、、std::uniqueおよび消去-削除イディオムを使用して、これらの重複する隣接する空白文字を削除できます。

// s = "       This       is       a sample   string     "  
s.erase(std::unique(std::begin(s), std::end(s), by_space), 
        std::end(s));
// s = " This is a sample string "  

これにより、前面や背面に余分な空白文字が残る可能性があります。これは非常に簡単に削除できます。

if (std::size(s) && std::isspace(s.back()))
    s.pop_back();

if (std::size(s) && std::isspace(s.front()))
    s.erase(0, 1);

これがデモです。


-1

STLメソッドを使用せず、C ++文字列自体のメソッドのみを使用するこの問題の解決策は、次のとおりです。

void processString(string &s) {
    if ( s.empty() ) return;

    //delete leading and trailing spaces of the input string
    int notSpaceStartPos = 0, notSpaceEndPos = s.length() - 1;
    while ( s[notSpaceStartPos] == ' ' ) ++notSpaceStartPos;
    while ( s[notSpaceEndPos] == ' ' ) --notSpaceEndPos;
    if ( notSpaceStartPos > notSpaceEndPos ) { s = ""; return; }
    s = s.substr(notSpaceStartPos, notSpaceEndPos - notSpaceStartPos + 1);

    //reduce multiple spaces between two words to a single space 
    string temp;
    for ( int i = 0; i < s.length(); i++ ) {
        if ( i > 0 && s[i] == ' ' && s[i-1] == ' ' ) continue;
        temp.push_back(s[i]);
    }
    s = temp;
}

私はこのメソッドを使用して、LeetCodeの問題を文字列内の単語を逆に渡しました


-1
void TrimWhitespaces(std::wstring& str)
{
    if (str.empty())
        return;

    const std::wstring& whitespace = L" \t";
    std::wstring::size_type strBegin = str.find_first_not_of(whitespace);
    std::wstring::size_type strEnd = str.find_last_not_of(whitespace);

    if (strBegin != std::wstring::npos || strEnd != std::wstring::npos)
    {
        strBegin == std::wstring::npos ? 0 : strBegin;
        strEnd == std::wstring::npos ? str.size() : 0;

        const auto strRange = strEnd - strBegin + 1;
        str.substr(strBegin, strRange).swap(str);
    }
    else if (str[0] == ' ' || str[0] == '\t')   // handles non-empty spaces-only or tabs-only
    {
        str = L"";
    }
}

void TrimWhitespacesTest()
{
    std::wstring EmptyStr = L"";
    std::wstring SpacesOnlyStr = L"    ";
    std::wstring TabsOnlyStr = L"           ";
    std::wstring RightSpacesStr = L"12345     ";
    std::wstring LeftSpacesStr = L"     12345";
    std::wstring NoSpacesStr = L"12345";

    TrimWhitespaces(EmptyStr);
    TrimWhitespaces(SpacesOnlyStr);
    TrimWhitespaces(TabsOnlyStr);
    TrimWhitespaces(RightSpacesStr);
    TrimWhitespaces(LeftSpacesStr);
    TrimWhitespaces(NoSpacesStr);

    assert(EmptyStr == L"");
    assert(SpacesOnlyStr == L"");
    assert(TabsOnlyStr == L"");
    assert(RightSpacesStr == L"12345");
    assert(LeftSpacesStr == L"12345");
    assert(NoSpacesStr == L"12345");
}

-2

何についての消去・削除イディオム

std::string s("...");
s.erase( std::remove(s.begin(), s.end(), ' '), s.end() );

ごめんなさい。すべての空白を削除たくないのは遅すぎました。


こんにちは。答えが間違っていることに気付いたので、必要に応じて削除できます。そうすれば、この回答でDVから失った担当者を取り戻すことができます:)
cigien
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.