What's the best way to trim std::string?

后端 未结 30 2882
无人及你
无人及你 2020-11-21 22:13

I\'m currently using the following code to right-trim all the std::strings in my programs:

std::string s;
s.erase(s.find_last_not_of(\" \\n\\r\\         


        
相关标签:
30条回答
  • 2020-11-21 22:34

    I'm not sure if your environment is the same, but in mine, the empty string case will cause the program to abort. I would either wrap that erase call with an if(!s.empty()) or use Boost as already mentioned.

    0 讨论(0)
  • 2020-11-21 22:37

    An elegant way of doing it can be like

    std::string & trim(std::string & str)
    {
       return ltrim(rtrim(str));
    }
    

    And the supportive functions are implemented as:

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

    And once you've all these in place, you can write this as well:

    std::string trim_copy(std::string const & str)
    {
       auto s = str;
       return ltrim(rtrim(s));
    }
    
    0 讨论(0)
  • 2020-11-21 22:38

    In the case of an empty string, your code assumes that adding 1 to string::npos gives 0. string::npos is of type string::size_type, which is unsigned. Thus, you are relying on the overflow behaviour of addition.

    0 讨论(0)
  • 2020-11-21 22:38

    Here's a solution easy to understand for beginners not used to write std:: everywhere and not yet familiar with const-correctness, iterators, STL algorithms, etc...

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

    Hope it helps...

    0 讨论(0)
  • 2020-11-21 22:41

    Bit late to the party, but never mind. Now C++11 is here, we have lambdas and auto variables. So my version, which also handles all-whitespace and empty strings, is:

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

    We could make a reverse iterator from wsfront and use that as the termination condition in the second find_if_not but that's only useful in the case of an all-whitespace string, and gcc 4.8 at least isn't smart enough to infer the type of the reverse iterator (std::string::const_reverse_iterator) with auto. I don't know how expensive constructing a reverse iterator is, so YMMV here. With this alteration, the code looks like this:

    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());
    }
    
    0 讨论(0)
  • 2020-11-21 22:41

    Contributing my solution to the noise. trim defaults to creating a new string and returning the modified one while trim_in_place modifies the string passed to it. The trim function supports c++11 move semantics.

    #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");  
    }
    
    0 讨论(0)
提交回复
热议问题