从字符串中移除前导空格和尾随空格

如何在 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 and substr,重复使用单词边界来获得所需的格式。
245582 次浏览

Boost string trim algorithm

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


[...]


std::string msg = "   some text  with spaces  ";
boost::algorithm::trim(msg);

这就是所谓的修剪。如果你能使用 Boost,我会推荐它。

否则,使用 find_first_not_of获取第一个非空格字符的索引,然后使用 find_last_not_of从末尾获取非空格字符的索引。使用这些函数,可以使用 substr获得没有周围空格的子字符串。

In response to your edit, I don't know the term but I'd guess something along the lines of "reduce", so that's what I called it. :) (Note, I've changed the white-space to be a parameter, for flexibility)

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

What about the 删除-删除成语?

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

对不起,我看到您不想删除 所有空格时已经太晚了。

我已经测试过了,一切正常。所以这个 processInput 方法只是要求用户输入一些内容。它将返回一个字符串,该字符串在内部没有额外的空格,在开始或结束处也没有额外的空格。希望这个能帮上忙。(也可以添加一堆评论,使其更容易理解)。

you can see how to implement it in the main() at the bottom

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

在一行中轻松删除 std: : 字符串的前导、尾随和额外空格

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

removing only leading spaces

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(" +"), " ");
/// 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);
}

In the above code, the isSpace() function is a boolean function that tells whether a character is a white space, you can implement this function to reflect your needs, or just call the isspace() from "ctype.h" if you want.

My Solution for this problem not using any STL methods but only C++ string's own methods is as following:

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 问题 Reverse Words in a String

This is my solution for stripping the leading and trailing spaces ...

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”

我目前正在使用以下功能:

// 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);
}
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");
}

例如按照 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"

使用标准库有许多好处,但是必须注意一些导致异常的特殊情况。例如,没有一个答案涉及 C + + 字符串具有某些 Unicode 字符的情况。在这种情况下,如果使用函数 是空间,将引发异常。

我一直在使用下面的代码来修整字符串和其他一些可能会派上用场的操作。这段代码的主要好处是: 它真的很快(比我测试过的任何代码都要快) ,它只使用标准库,而且从来不会导致异常:

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

对于前导空格和尾随空格,可以这样做:

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

你可以这样做:

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

修剪前导空格和尾随空格的示例

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

OR

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

这可能是最简单的。

可以使用 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);
}
}

为什么这么复杂?

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

This works even if boost was to fail, no regex, no weird stuff nor libraries.

编辑: 修正 M.M. 的评论。

干净利落

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

没有 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);
}

C++17 introduced std::basic_string_view, a class template that refers to a constant contiguous sequence of char-like objects, i.e. a view of the string. Apart from having a very similar interface to std::basic_string, it has two additional functions: remove_prefix(), which shrinks the view by moving its start forward; and remove_suffix(), which shrinks the view by moving its end backward. These can be used to trim leading and trailing space:

#include <string_view>
#include <string>


std::string_view ltrim(std::string_view str)
{
const auto pos(str.find_first_not_of(" \t\n\r\f\v"));
str.remove_prefix(std::min(pos, str.length()));
return str;
}


std::string_view rtrim(std::string_view str)
{
const auto pos(str.find_last_not_of(" \t\n\r\f\v"));
str.remove_suffix(std::min(str.length() - pos - 1, str.length()));
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
std::string s1{ sv1 };
std::string s2{ sv2 };
std::string s3{ sv3 };
}

注意: 使用 std::min来确保 pos不大于 size(),当字符串中的所有字符都是空格且 find_first_not_of返回 npos时会发生这种情况。而且,std::string_view是一个非所有引用,因此它只有在原始字符串仍然存在的情况下才有效。修整字符串视图对其所基于的字符串没有影响。

若要添加问题,请说明如何扩展此格式以处理字符串的单词之间的额外空格。

Actually, this is a simpler case than accounting for multiple leading and trailing white-space characters. All you need to do is remove duplicate adjacent white-space characters from the entire string.

相邻空格的谓词可以简单地表示:

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

Here's a 小样.

通过在字符串中使用 pop_back()函数,可以实现移除前导空间和尾随空间的时间和空间复杂度不变。守则如下:

void trimTrailingSpaces(string& s) {
while (s.size() > 0 && s.back() == ' ') {
s.pop_back();
}
}


void trimSpaces(string& s) {
//trim trailing spaces.
trimTrailingSpaces(s);
//trim leading spaces
//To reduce complexity, reversing and removing trailing spaces
//and again reversing back
reverse(s.begin(), s.end());
trimTrailingSpaces(s);
reverse(s.begin(), s.end());
}

This was the most intuitive way for me to solve this problem:

/**
* @brief Reverses a string, a helper function to removeLeadingTrailingSpaces
*
* @param line
* @return std::string
*/
std::string reverseString (std::string line) {
std::string reverse_line = "";


for(int i = line.length() - 1; i > -1; i--) {
reverse_line += line[i];
}


return reverse_line;
}


/**
* @brief Removes leading and trailing whitespace
* as well as extra whitespace within the line
*
* @param line
* @return std::string
*/
std::string removeLeadingTrailingSpaces(std::string line) {


std::string filtered_line = "";
std::string curr_line = line;


for(int loop = 0; loop < 2; loop++) {


bool leading_spaces_exist = true;
filtered_line = "";
std::string prev_char = "";


for(int i = 0; i < line.length(); i++) {


// Ignores leading whitespace
if(leading_spaces_exist) {
if(curr_line[i] != ' ') {
leading_spaces_exist = false;
}
}


// Puts the rest of the line in a variable
// and ignore back-to-back whitespace
if(!leading_spaces_exist) {
if(!(curr_line[i] == ' ' && prev_char == " ")) {
filtered_line += curr_line[i];
}
prev_char = curr_line[i];
}
}


/*
Reverses the line so that after we remove the leading whitespace
the trailing whitespace becomes the leading whitespace.
            

After the second round, it needs to reverse the string back to
its regular order.
*/
curr_line = reverseString(filtered_line);
}


return curr_line;
}

基本上,我循环遍历字符串并删除前导空格,然后翻转字符串并重复相同的过程,然后翻转回正常。

我还添加了清理线路的功能,如果有背靠背的空间。