std :: string을 다듬는 가장 좋은 방법은 무엇입니까?
현재 다음 코드를 사용하여 std::strings
내 프로그램의 모든 항목을 오른쪽 트리밍합니다 .
std::string s;
s.erase(s.find_last_not_of(" \n\r\t")+1);
잘 작동하지만 실패 할 수있는 최종 사례가 있는지 궁금합니다.
물론 우아한 대안과 제안 솔루션에 대한 답변도 환영합니다.
편집 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 <rim(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;
}
위의 원래 답변은 맥락과 높은 투표를 계속 사용할 수 있습니다. 유지하기 위해 유지하고 있습니다.
사용 부스트의 높은 알고리즘은 쉬운 것입니다 :
#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
하면 공백이 아닌 사용자 지정 조건 음색하는 모든 문자를 사용할 수 있습니다.
다음 코드를 사용하여 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 );
}
파티에 조금 늦었지만 신경 쓰지 이제 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
있습니다. 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());
}
당신이하는 일은 훌륭하고 강력합니다. 나는 오랫동안 같은 방법을 사용했지만 아직 더 빠른 방법을 찾지 못했습니다.
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);
}
트리밍 할 문자를 제공하면 공백이 아닌 문자를 트리밍 할 수있는 유연성과 트리밍 할 문자 만 트리밍 할 수있는 효율성이 있습니다.
시도해 목적.
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;
}
나는 tzaman의 솔루션을 좋아하는데, 문제는 공백 만 포함하는 유일한 자르지 않는다는 것입니다.
1 개의 결함을 수정하신 후 2 개의 트리머 라인 사이에 str.clear ()를 추가하십시오.
std::stringstream trimmer;
trimmer << str;
str.clear();
trimmer >> str;
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을 추가하여 string::npos
0 을 제공 합니다 가정합니다 . string::npos
형식 string::size_type
은 서명되지 않습니다. 따라서 덧셈의 오버플로 동작에 의존합니다.
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 케이스 작동합니다. :-)
@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);
}
내 대답은 제어 문자와 공백 ( 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
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));
}
이것이 내가 사용하는 것입니다. 앞쪽에서 계속 공간을 제거하고 남은 것이 있으면 똑같이하십시오.
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
}
그만한 가치가있는 성능을 고려한 기능 구현입니다. 내가 본 다른 많은 루틴보다 훨씬 빠 사용합니다. 반복기와 std :: finds를 사용하는 대신 사용하는 대신 사용합니다. 다음과 같은 특수한 경우를 최적화합니다 : 크기가 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));
}
}
s.erase(0, s.find_first_not_of(" \n\r\t"));
s.erase(s.find_last_not_of(" \n\r\t")+1);
그것을하는 우아한 방법은 다음과 가변합니다.
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));
}
C ++ 17에서는 basic_string_view :: remove_prefix 및 basic_string_view :: remove_suffix를 사용할 수 있습니다 .
std::string_view trim(std::string_view s) const
{
s.remove_prefix(std::min(s.find_first_not_of(" \t\r\v\n"), s.size()));
s.remove_suffix((s.size() - 1) - std::min(s.find_last_not_of(" \t\r\v\n"), s.size() - 1));
return s;
}
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());
}
업그레이드를 다듬는 "최선의 방법"을 요청하기 시작하면 다음과 같은 좋은 구현이 될 것입니다.
- 임시 디렉토리를 할당하지 않습니다.
- 내부 언어 및 음악에 대한 문구가 있습니다.
- 다양한 시퀀스 시퀀스 / 수용을 수용 가능한 쉽게 사용자 정의 가능
분명히 이것에 접근하는 데는 너무 많은 다른 방법이 설치되어 있습니다. 그러나 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;
}
귀하의 환경이 동일한 지 확실하지 않지만 내 경우에 빈번하게 발생하는 프로그램이 중단됩니다. 그 지우기 호출을 if (! s.empty ())로 사용하거나 이미 언급했듯이 Boost를 사용합니다.
내가 생각하는 것은 다음과 가변적이다.
std::stringstream trimmer;
trimmer << str;
trimmer >> str;
스트림 추출은 공백을 자동으로 제거 할 때 매력처럼 작동합니다.
내가 그렇게 말하면 꽤 깨끗하고 우아합니다. ;)
소음에 대한 내 솔루션을 제공합니다. 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");
}
이 작업은 및 추가로 인해 C ++ 11에서 더 간단하게 수행 할 수 있습니다 .back()
pop_back()
while ( !s.empty() && isspace(s.back()) ) s.pop_back();
내 버전은 다음과 달라집니다.
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);
위의 방법은 훌륭하지만 일상이 공백으로 사용할 기능의 조합을 사용하고 싶을 때 있습니다. 이 경우 펑터를 사용하여 지저분 할 작업에 사용할 수있는 간단한 루프를 선호합니다. 여기에 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);
}
다음은 간단한 구현입니다. 가장 간단한 작업을 위해 특수 구조를 사용합니다. 내장 된 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);
}
여기에 std::
모든 곳 에서 익숙하지 않고 아직-정확함 const
, iterator
s, 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));
}
도움이 되셨기를 바랍니다 ...
이 버전은 내부 공백과 영숫자가 아닌 문자를 제거합니다.
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;
}
또 다른 옵션-양쪽 끝에서 하나 이상의 문자를 제거합니다.
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);
}
참고 URL : https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring
'ProgramingTip' 카테고리의 다른 글
git log 또는 git diff를 종료하는 방법 (0) | 2020.09.29 |
---|---|
하나의 CSS 파일을 다른 파일에 포함 할 수 있습니까? (0) | 2020.09.29 |
Visual Studio의 프로젝트에 기존 디렉터리 트리를 추가 비용하나요? (0) | 2020.09.29 |
div 블록 내부의 CSS 중앙 텍스트 (가로 및 세로) (0) | 2020.09.29 |
Android Studio의 코드 단축키 (0) | 2020.09.29 |