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_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 :: nposstd :: 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.