Какой лучший способ обрезать std :: string? - PullRequest
722 голосов
/ 19 октября 2008

В настоящее время я использую следующий код, чтобы обрезать вправо все std::strings в моих программах:

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

Работает нормально, но мне интересно, есть ли какие-нибудь конечные случаи, когда он может потерпеть неудачу?

Конечно, ответы с изящными альтернативами, а также с левым решением приветствуются.

Ответы [ 40 ]

3 голосов
/ 27 мая 2010

Вот что я придумал:

std::stringstream trimmer;
trimmer << str;
trimmer >> str;

Потоковое извлечение автоматически удаляет пробелы, поэтому это работает как шарм.
Довольно чистый и элегантный, если я так скажу. ;)

3 голосов
/ 31 августа 2016

Вот моя версия:

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);
3 голосов
/ 06 мая 2014

Внесение моего решения в шум. 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 голосов
/ 20 октября 2008

Я не уверен, что ваша среда такая же, но в моем случае пустая строка приведет к прерыванию работы программы. Я бы либо обернул этот вызов стирания с помощью if (! S.empty ()), либо использовал Boost, как уже упоминалось.

3 голосов
/ 31 января 2016

Это проще сделать в C ++ 11 за счет добавления back() и pop_back().

while ( !s.empty() && isspace(s.back()) ) s.pop_back();
2 голосов
/ 24 ноября 2009

Вышеуказанные методы хороши, но иногда вы хотите использовать комбинацию функций для того, что в вашей процедуре считается пробелом. В этом случае использование функторов для объединения операций может стать беспорядочным, поэтому я предпочитаю простой цикл, который можно изменить для обрезки. Вот слегка измененная функция обрезки, скопированная с версии C здесь, на SO. В этом примере я обрезаю не буквенно-цифровые символы.

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);
}
1 голос
/ 08 декабря 2016

Вот решение, которое легко понять для начинающих, которые не привыкли писать std:: везде и еще не знакомы с const -корректностью, iterator s, STL algorithm s и т. Д. *

#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 голос
/ 23 марта 2016

Вот прямая реализация. Для такой простой операции вам, вероятно, не следует использовать какие-либо специальные конструкции. Встроенная функция 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);
}
1 голос
/ 15 марта 2016

Поскольку я хотел обновить свою старую функцию обрезки C ++ с помощью подхода C ++ 11, я протестировал множество опубликованных ответов на этот вопрос. Я пришел к выводу, что я сохранил свое старое решение C ++!

Это самый быстрый по большому счету, даже добавление большего количества символов для проверки (например, \ r \ n я не вижу варианта использования для \ f \ v) все же быстрее, чем решения, использующие алгоритм.

     std::string & trimMe (std::string & str)
     {
        // right trim
        while (str.length () > 0 && (str [str.length ()-1] == ' ' || str [str.length ()-1] == '\t'))
           str.erase (str.length ()-1, 1);

        // left trim
        while (str.length () > 0 && (str [0] == ' ' || str [0] == '\t'))
           str.erase (0, 1);
        return str;
     }
1 голос
/ 24 августа 2010

Эта версия урезает внутренние пробелы и не алфавитно-цифровые символы:

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