문자열에서 선행 및 후행 공백 제거


94

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"  

솔루션에 언급 된 문자열 메서드를 사용하면 이러한 작업을 두 단계로 수행 할 수 있습니다.

  1. 선행 및 후행 공백을 제거하십시오.
  2. 원하는 형식을 얻으려면 단어 경계에서 find_first_of, find_last_of, find_first_not_of, find_last_not_of 및 substr 을 반복적으로 사용하십시오 .

답변:


128

이를 트리밍이라고합니다. Boost 를 사용할 수 있다면 추천합니다.

그렇지 않으면를 사용 find_first_not_of하여 공백이 아닌 첫 번째 문자 find_last_not_of의 인덱스를 가져온 다음 공백이 아닌 끝에서 인덱스를 가져옵니다. 이를 사용 substr하여 주변 공백이없는 하위 문자열을 가져옵니다.

당신의 편집에 대한 응답으로, 나는 그 용어를 모르지만 "reduce"라인을 따라 무언가를 추측 할 것입니다. 그래서 제가 그것을 부르는 것입니다. :) (참고, 유연성을 위해 공백을 매개 변수로 변경했습니다.)

#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'를 의미한다고 가정합니다. 그리고 당신은 부분 문자열에 대해 하나씩 떨어져 있습니다. substr (beginStr, endStr-beginStr + 1);
goldPseudo

해야 site_tsize_t? 그리고 주석이있는 곳 no whitespace은 문자열이 모두 공백이거나 비어 있음을 의미 한다고 생각합니다 .
Fred Larson

감사 size_t합니다. 편집에서 오타와 하나씩 수정했지만 내 댓글이 반전 된 것을 알지 못했습니다. 감사합니다.
GManNickG

@GMan 매우 우아한 솔루션. 감사.
Ankur

버그 : trim ()을 통해 "one \ ttwo"를 실행 해보십시오. 결과는 빈 문자열입니다. std :: string :: npos에 대해서도 endStr을 테스트해야합니다.
dlchambers

48

한 줄로 std :: string에서 선행, 후행 및 추가 공백을 쉽게 제거

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에서만 작동합니다.
Martin Pecka

7
탭을 제거하지는 않지만 고칠 수 있습니다. 고칠 수없는 것은 매우 느리다는 것입니다 ( substr또는로 대답하는 것보다 ~ 100 배 느립니다 erase).
4LegsDrivenCat 2015-10-30

속도 최적화 정규식 최적의 솔루션 아니지만, 한 번 정규식의 인스턴스를 생성하여 개선 될 수
예브게니 카르 포브

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

결과는 "Plamen"입니다.


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

부스트를 사용하라는 jon-hanson의 제안에 따라 선행 및 후행 공백을 자르는 예 (후행 및 보류중인 공백 만 제거) :

#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

2
문자열에 공백 만 있으면 깨집니다
DAG

3

표준 라이브러리를 사용하면 많은 이점이 있지만 예외를 발생시키는 몇 가지 특별한 경우를 알고 있어야합니다. 예를 들어, C ++ 문자열에 유니 코드 문자가있는 경우에 대한 답변은 없습니다. 이 경우 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의 의견 수정.


이것은 공백의 길이를 계산하고 각 끝에 대해 ​​단일 지우기 호출을 사용하는 것과 비교하면 다소 비효율적입니다.
MM

1

C ++ 17 도입 std::basic_string_view, 문자와 유사한 객체의 연속적인 연속 시퀀스, 즉 문자열보기를 참조하는 클래스 템플릿. 와 매우 유사한 인터페이스 std::basic_string외에도 두 가지 추가 기능이 있습니다. 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, 및 erase-remove 관용구 를 사용하여 중복 된 인접 공백 문자를 제거 할 수 있습니다 .

// 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.