std :: string을 자르는 가장 좋은 방법은 무엇입니까?


812

현재 다음 코드를 사용하여 std::strings프로그램의 모든 항목을 오른쪽으로 자릅니다 .

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

잘 작동하지만 실패 할 수있는 최종 사례가 있는지 궁금합니다.

물론, 우아한 대안과 왼쪽 손질 솔루션으로 답변을 환영합니다.


549
이 질문에 대한 답은 C ++ 표준 라이브러리가 얼마나 부족한 지에 대한 증거입니다.
— Idan K

83
@IdanK 그리고 C ++ 11에는 여전히이 기능이 없습니다.
— Quantum

44
@IdanK : 좋아, 그렇지 않아! "의 한 사람의 아이디어에 의해 방해 우리의 처분에 우리가 지금 가지고있는 모든 경쟁 옵션, 봐 우리가해야 할 그런 식으로!"
— 궤도에서 가벼움 경주

59
@LightnessRacesinOrbit 유형 내의 기능, 디자인 결정이며, 문자열에 트림 기능을 추가하는 것이 어쨌든 최선의 해결책은 아니지만 모든 표준 방법을 제공하지는 않지만 모든 사람이 걱정할 수 있습니다. 같은 작은 문제가 계속해서 반복되고
— 있더라도,

27
std::string다른 언어를 사용하기에 좋은 함수 (예 : Python)와 같은 함수 인 경우 트리밍 함수가 클래스에 내장되지 않은 이유에 대해 의문을 제기 할 수 있습니다 .
— HelloGoodbye

답변:


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 가지 중 하나를 사용하는 경향이 있습니다.

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

그들은 상당히 자명하고 잘 작동합니다.

편집 : BTW, 실제로 로케일을 지원하는 두 번째 정의가 있기 때문에 std::ptr_fun명확성을 돕기 위해 거기에 std::isspace있습니다. 이것은 똑같은 캐스트 일 수 있었지만 나는 이것을 더 좋아하는 경향이 있습니다.

편집 : 매개 변수를 참조하여 수락하고 수정하고 반환하는 것에 대한 의견을 제시합니다. 동의한다. 내가 선호하는 구현은 두 가지 기능 세트, 하나는 제자리에 있고 다른 하나는 사본을 만듭니다. 더 나은 예는 다음과 같습니다.

#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문제를 해결하기 위해 사용 했다.
— Tom

5
참조 대신 포인터를 사용하므로 콜 포인트에서 이러한 함수가 복사본을 만드는 대신 문자열을 편집하는 것을 이해하기가 훨씬 쉽습니다.
— Marco Leogrande

3
isspace와 함께 쉽게 비 ASCII 문자 정의되지 않은 동작 얻을 수 있습니다 stacked-crooked.com/view?id=49bf8b0759f0dd36dffdad47663ac69f
— R. 마르틴 페르난데스

10
왜 정적인가? 익명 네임 스페이스가 선호되는 곳입니까?
— Trevor Hickey

3
@TrevorHickey, 원하는 경우 익명 네임 스페이스를 대신 사용할 수 있습니다.
— 에반 테란

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

117
부스트는 작은 문제에 대한 거대한 망치입니다.
— Casey Rodarmor 2016 년

143
@rodarmor : Boost는 많은 작은 문제를 해결합니다. 그것은 많은 것을 해결하는 거대한 망치입니다.
— Nicol Bolas

123
부스트는 다양한 문제를 해결하는 다양한 크기의 망치 세트입니다.
— 이브라힘

11
@rodarmor 부스트는 헤더 중 하나를 포함하면 프로그램에 전체가 영향을 미치는 완전히 또는 전혀없는 단일체처럼 말한다. 분명히 그렇지 않습니다. Btw, 나는 Boost, fwiw를 사용한 적이 없다.
— underscore_d

61

다음 코드를 사용하여 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 );
}

4
이것은 줄 바꿈, 줄 바꿈, 특히 캐리지 리턴과 같은 다른 형태의 공백을 감지하지 못합니다.
— Tom

1
권리. 자르려는 공백에 맞게 사용자 정의해야합니다. 내 특정 응용 프로그램은 공백과 탭만 기대했지만 다른 것을 잡기 위해 \ n \ r을 추가 할 수 있습니다.
— Bill the Lizard

5
str.substr(...).swap(str)더 나은. 과제를 저장합니다.
— updogliu

4
@updogliu 이동 할당을 사용하지 basic_string& operator= (basic_string&& str) noexcept;않습니까?
— nurettin

8
이 답변은 모든 공백 인 문자열을 변경하지 않습니다. 어느 것이 실패입니다.
— Tom Andersen

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 CITBL

CharT에서 basic_string과 template을 사용하면 모든 문자열에 대해이 작업을 수행 할 수 있습니다. 공백에 템플릿 변수를 사용하면 ws <CharT>처럼 사용할 수 있습니다. 기술적으로 그 시점에서 c ++ 20을 준비하고 인라인을 의미하는 constexpr로 표시 할 수 있습니다
— Beached

@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 두 번째 터미네이션 조건으로 사용할 수 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은 trim ()을 std :: string에 도입하지 않아 모든 사람이 인생을 더 쉽게 만들었습니다.
— 밀라노 Babuškov

3
나는 항상 대신 그것을 구현하는, 트림 문자열을 하나의 함수 호출을 원하는
— linquize

22
가치있는 것을 위해 그 람다를 사용할 필요가 없습니다. 당신은 그냥 전달할 수 있습니다 std::isspace:auto wsfront=std::find_if_not(s.begin(),s.end(),std::isspace);
— vmrob

4
O (N) 문자열 복사본을 하나만 수행하는 구현의 유일한 대답은 +1입니다.
— Alexei Averchenko 9

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

3
@rgove 설명해주세요. str.find_last_not_of(x)x와 같지 않은 첫 번째 문자의 위치를 ​​반환합니다. x와 일치하는 문자가 없으면 npos 만 반환합니다. 이 예에서 접미사 공백이 없으면에 해당하는 값을 반환 str.length() - 1하여 본질적으로 str.erase((str.length() - 1) + 1).실수하지 않는 한 본질적으로 산출 합니다.
— 트래비스

5
이것은 복사 생성자를 불필요하게 호출하지 않도록 std :: string &을 리턴해야합니다.
— heksesang

7
이것이 반환 매개 변수를 수정 한 후 왜 복사본을 반환하는지 혼란 스럽습니까?
— Galik

3
@MiloDC 혼란스러워서 참조 대신 사본을 반환하는 이유 입니다. 돌아 오는 것이 더 의미가 std::string&있습니다.
— Galik

25

나는 tzaman의 솔루션을 좋아하는데, 유일한 문제는 공백 만 포함하는 문자열을 자르지 않는다는 것입니다.

1 개의 결함을 수정하려면 2 개의 트리머 라인 사이에 str.clear ()를 추가하십시오.

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

니스 :) 두 솔루션의 문제점은 양쪽 끝을 다듬을 것입니다. ltrim또는 rtrim이와 같이 만들 수 없습니다 .
— tzaman

44
좋지만 내부 공백이있는 문자열을 처리 할 수 ​​없습니다. 예 : trim (abc def ")-> abc, abc 만 남음
— liheyuan

내부 공백이 없다는 것을 알고 있다면 좋은 해결책입니다!
— 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

고마워요, 나를 위해 일했습니다 : std : string s = "오 noez : 우주 \ r \ n"; std :: string clean = 트림 (들);
— Alexx Roche

15

빈 문자열의 경우 코드에서 1을 추가하여 string::npos0 을 제공 하는 것은 부호없는 string::npos유형 string::size_type인 것으로 가정합니다 . 따라서 오버플로 추가 동작에 의존하고 있습니다.


23
마치 나쁜 것처럼 말하고 있습니다. 부호있는 정수 오버플로 동작이 잘못되었습니다.
— MSalters

2
에 추가 1하려면 에 따라 제공 std::string::npos 해야0 합니다 C++ Standard. 따라서 절대적으로 신뢰할 수있는 좋은 가정입니다.
— Galik

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 7:14의

1
^ find_first_not_of를 사용 하시겠습니까? 수정하기가 비교적 쉽습니다.
— Abhinav Gauniyal 2018 년

13

C ++ 17에서는 basic_string_view :: remove_prefix 및 basic_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_of 는 std :: string :: npos 와 std :: string_view :: size 를 리턴 할 것이다. 최소값은 4를 리턴 할 것이다. std :: string_view :: remove_prefix에 의해 제거되었습니다 . gcc 9.2와 clang 9.0은 모두 이것을 올바르게 처리합니다. godbolt.org/z/DcZbFH
— Phidelux

1
감사! 나에게 좋아 보인다.
— 콘 탄고

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문자에 그래픽 표현이 있는지 여부를 결정하므로이를 사용하여 문자열의 양쪽에서 그래픽 표현이없는 문자를 제거하도록 Evan의 답변을 변경할 수 있습니다. 결과는 훨씬 더 우아한 솔루션입니다.

#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

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 표준 라이브러리에는 여전히 memchr과 같은 <string.h>에 매우 유용한 기능이 있습니다. 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

3

내 솔루션을 소음에 기여합니다. 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");  
}

3

이것은 back()and 의 추가로 인해 C ++ 11에서 더 간단하게 수행 할 수 있습니다 pop_back().

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

OP가 제안한 접근 방식도 나쁘지 않습니다. 추적하기가 조금 더 어렵습니다.
— nobar

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

또 다른 옵션-양쪽 끝에서 하나 이상의 문자를 제거합니다.

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.