【问题标题】:How can I separate numbers and letters in a C++ String?如何在C ++字符串中分隔数字和字母?
【发布时间】:2014-12-17 14:33:31
【问题描述】:

我有几个包含数字和字母的输入字符串。有时缺少空间。每次字符串从数字变为字母或从字母变为数字时,我想添加一个额外的空格。

示例输入:

"30EinsteinStreet"
"548 Roe Drive5500 TestCity"
"44B SaarlouisDrive1234Testtown"

他们应该变成:

"30 EinsteinStreet"
"548 Roe Drive 5500 TestCity"
"44 B SaarlouisDrive 1234 Testtown"

我现有的功能不起作用,我认为这很复杂。谁能提供一个简单的解决方案?最好使用现代 C++11 类,但不使用 Boost。另外我使用的是 GCC,所以所有的正则表达式都对我不起作用。

谢谢

这是我现有的方法:

        inline string separateAlphanumChunks(const std::string& s) 
        {
            string ret = "";
            const int sl = s.length();
            //int inserts = 0;

            if (sl<=4)
                return s;

            for (int i=0 ; i< sl ; i++)
            {
//              cerr << "separateAlphanumChunks: '" << ret << "'" <<endl;
                // check if index would go out of range
                if (i+4 > sl)
                {
                    ret += s.substr (i,sl-i);
                    //TODO add the remain to ret
                    break;
                }

                // seperate chars
                const char c0 = s[i+0];
                const char c1 = s[i+1];

                // check if 0 and 1 are the same class
                const bool c0c = isCharAnInt (c0);
                const bool c1c = isCharAnInt (c1);
                bool class0 = false;
                if (c0c == c1c)
                {
                    class0 = c0c;
                }
                else
                {
                    ret += c0;
//                  cerr << "cont1: '" << c0 << "'" <<endl;
                    continue;
                }

                // seperate chars
                const char c2 = s[i+2];
                const char c3 = s[i+3];

                // check if 2 and 3 are the same class
                const bool c2c = isCharAnInt (c2);
                const bool c3c = isCharAnInt (c3);
                bool class2 = false;
                if (c2c == c3c)
                {
                    class2 = c2c;
                }
                else
                {
                    ret += c0;
//                  cerr << "cont2: '" << c0 << "'" <<endl;
                    continue;
                }

                // check if the 2 classes are different
                if (class0 != class2)
                {
                    // split!
                    ret += c0+(c1+string(" ")+c2)+c3;
                    //inserts++;
                    i+=3;
                }
                else
                {
                    ret += c0;
//                  cerr << "cont3: '" << c0 << "'" <<endl;
                    continue;
                }

            }

            // remove double spaces
            //replaceStringInPlace(ret, "  "," "); 
            //cerr << "separateAlphanumChunks: '" << ret << "'" <<endl;
            return ret;
        }

    inline bool isCharAnInt (char c)
    {
        //TODO might be able to use isdigit() here
        int i = c - '0';
        return ((i>=0) && (i<=9));
    }

【问题讨论】:

  • std::regex 和/或std::regex_iterator
  • @stijn 我忘了说,我没有研究任何正则表达式函数,因为我使用的是 GCC。 在 GCC 中仅部分实现且存在缺陷。 google.com.au/search?q=std::regex+gcc ...我会解决这个问题。
  • @KenyakornKetsombut:那么,Boost.Regex 怎么样?
  • @KenyakornKetsombut:不,不是。 GCC 4.9 是几个月前发布的。

标签: c++ string algorithm c++11 gcc


【解决方案1】:

我看到了各种复杂的答案,这也是给出另一个答案的原因。 您的问题的答案正是在问题陈述中: "每次字符串从数字变为字母或从字母变为数字时添加一个额外的空格。"

所以这正是你想要的(我使用了之前答案中的一些代码)编译应该使用标志 -std=c++11

#include <string>
#include <iostream>
using namespace std;

enum charTypeT{ other, alpha, digit};

charTypeT charType(char c){
    if(isdigit(c))return digit;
    if(isalpha(c))return alpha;
    return other;
}

string separateThem(string inString){
  string oString = "";charTypeT st=other;
    for(auto c:inString){
        if( (st==alpha && charType(c)==digit) || (st==digit && charType(c)==alpha) )
          oString.push_back(' ');
        oString.push_back(c);st=charType(c);
    }
    return oString;
}

int main(){
  string str1 = "30EinsteinStreet";
  string str2 = "548 Roe Drive5500 TestCity";
  string str3 = "44B SaarlouisDrive1234Testtown";

  cout << separateThem(str1) << endl;
  cout << separateThem(str2) << endl;
  cout << separateThem(str3) << endl;
}

【讨论】:

    【解决方案2】:

    我认为您正在寻找什么以及 Ajay 暗示的是一个 finite-state machine 来解析字符串。 虽然这不是 C++11 解决方案,您可能会通过正则表达式找到更优雅的解决方案,但我在下面提供了代码示例。

    #include <iostream>
    #include <sstream>
    
    bool isDigit(const char c)
    {
        bool res = true;
        switch (c)
        {
            case '0': case '1': case '2': case '3': case '4':
            case '5': case '6': case '7': case '8': case '9':
                break;
    
            default:
                res = false;
                break;
        }
        return res;
    }
    
    std::string separateNumbers(const std::string& inputString)
    {
        const size_t N = inputString.length();
        std::ostringstream os;
        bool readDigit = false;
        for (size_t i = 0; i < N; ++i)
        {
            if (isDigit(inputString[i]))
            {
                if ((i > 0) && (i < N) && (! readDigit))
                {
                    if (inputString[i] != ' ')
                        os << ' ';
                }
                readDigit = true;
            }
            else
            {
                if ((i > 0) && (i < N) && (readDigit))
                {
                    if (inputString[i] != ' ')
                        os << ' ';
                }
                readDigit = false;
            }
            os << inputString[i];
        }
        return os.str();
    }
    
    int main(int argc, char** argv)
    {
        std::string strings[3] = {
            "30EinsteinStreet",
            "548 Roe Drive5500 TestCity",
            "44B SaarlouisDrive1234Testtown"
        };
    
        for (int i = 0; i < 3; ++i)
        {
            std::cout << "input #" << i << ": " << strings[i] << std::endl;
            std::cout << "output #" << i << ": " << separateNumbers(strings[i]) << std::endl;
            std::cout << std::endl;
        }
    
        return 0;
    }
    

    【讨论】:

    • 顺便说一句:当然,您可以重复使用您的函数 isCharAnInt()。
    • 您的 isDigit 实施很糟糕。 c&gt;='0' &amp;&amp; c&lt;='9'就够了
    • @Ajay:只有当你在健壮性和可移植性上加盖印章并且愉快地假设 ASCII 时。
    【解决方案3】:

    我的建议是通过字符串元素进行迭代。 类似的东西会有所帮助:

    #include <string>
    #include <iostream>
    using namespace std;
    
    string separateThem(string inString){
      string numbers = "1234567890";
      string oString = "";
      int i;
      for(i=0; i<inString.size()-1; i++){
        if ((numbers.find(inString[i]) != string::npos) && (numbers.find(inString[i+1]) == string::npos) && !isspace(inString[i+1])){
          oString += inString.substr(i,1) + " ";
        }
        else if ((numbers.find(inString[i]) == string::npos) && (numbers.find(inString[i+1]) != string::npos) && !isspace(inString[i+1])){
          oString += inString.substr(i,1) + " ";
        }
    else oString += inString.substr(i,1);
      }
      oString += inString.substr(i,1);
      return oString;
    }
    
    int main(){
      string str1 = "30EinsteinStreet";
      string str2 = "548 Roe Drive5500 TestCity";
      string str3 = "44B SaarlouisDrive1234Testtown";
    
      cout << separateThem(str1) << endl;
      cout << separateThem(str2) << endl;
      cout << separateThem(str3) << endl;
    }
    

    如果你执行这个,输出将是:

    30 EinsteinStreet
    548 Roe Drive 5500 TestCity
    44 B SaarlouisDrive 1234 Testtown
    

    希望这会有所帮助:)

    【讨论】:

      【解决方案4】:

      这是我的五分钱。

      #include <iostream>
      #include <string>
      #include <cctype>
      
      std::string SeparateAlphanumChunks( const std::string &s )
      {
          std::string::size_type n = 0;
          bool ctype = std::isdigit( s[0] );
      
          for ( char c : s )
          {
              if ( !ctype != !std::isdigit( c ) )
              {
                  ctype = std::isdigit( c );
                  if ( !isblank( c ) ) ++n;
              }
          }
      
          std::string t;
          t.reserve( s.size() + n );
      
          ctype = std::isdigit( s[0] );
      
          for ( char c : s )
          {
              if ( !ctype != !std::isdigit( c ) )
              {
                  ctype = std::isdigit( c );
                  if ( !isblank( c ) ) t.push_back( ' ');
              }
              t.push_back( c );
          }
      
          return t;
      }
      
      int main() 
      {
          for ( const std::string &s : { "30EinsteinStreet",
                                         "548 Roe Drive5500 TestCity",
                                         "44B SaarlouisDrive1234Testtown" 
                                       } )
          {
              std::cout << SeparateAlphanumChunks( s ) << std::endl;
          }
      
          return 0;
      }
      

      输出是

      30 EinsteinStreet
      548 Roe Drive 5500 TestCity
      44 B SaarlouisDrive 1234 Testtown
      

      您也可以“就地”更改字符串。例如

      #include <iostream>
      #include <string>
      #include <cctype>
      
      std::string & SeparateAlphanumChunks( std::string &s )
      {
          std::string::size_type n = 0;
          bool ctype = std::isdigit( s[0] );
      
          for ( char c : s )
          {
              if ( !ctype != !std::isdigit( c ) )
              {
                  ctype = std::isdigit( c );
                  if ( !isblank( c ) ) ++n;
              }
          }
      
          s.reserve( s.size() + n );
      
          ctype = std::isdigit( s[0] );
      
          for ( std::string::size_type i = 0; i < s.size(); i++ )
          {
              if ( !ctype != !std::isdigit( s[i] ) )
              {
                  ctype = std::isdigit( s[i] );
                  if ( !isblank( s[i] ) ) 
                  {
                      s.insert( i, 1, ' ' );
                  }
              }
          }
      
          return s;
      }
      
      
      int main() 
      {
          for ( std::string s : { "30EinsteinStreet",
                                  "548 Roe Drive5500 TestCity",
                                      "44B SaarlouisDrive1234Testtown" 
                                    } )
          {
              std::cout << SeparateAlphanumChunks( s ) << std::endl;
          }
      
          return 0;
      }
      

      【讨论】:

        【解决方案5】:

        升级到 GCC 4.9(它的第一个版本早在 4 月发布)并使用简单的正则表达式:

        #include <regex>
        #include <iostream>
        
        std::string fix(const std::string& in)
        {
            return std::regex_replace(
                in,
                std::regex("(?:([a-zA-Z])([0-9]))|(?:([0-9])([a-zA-Z]))"),
                "\\1\\3 \\2\\4",
                std::regex_constants::format_sed
            );
        }
        
        int main()
        {
            const std::string in[] = {
                "30EinsteinStreet",
                "548 Roe Drive5500 TestCity",
                "44B SaarlouisDrive1234Testtown"
            };
        
            for (auto el : in)
                std::cout << fix(el) << '\n';
        }
        
        /*
        "30 EinsteinStreet"
        "548 Roe Drive 5500 TestCity"
        "44 B SaarlouisDrive 1234 Testtown"
        */
        

        (live demo)

        【讨论】:

        • 我不太清楚为什么 std::regex("(?:([:alpha:])([:digit:]))|(?:([:digit:])([:alpha:]))") 不起作用,请注意。
        【解决方案6】:

        我建议您将string 迭代为原始字符串(即string::c_str()),并完全生成一个新字符串。这将是我的算法(不是很完整):

        • 对于每个字符,检查它是否为数字。如果没有,只需追加到新字符串。
        • 如果是,检查它是否是第一个字符 - 如果是,则追加到新字符串。
        • 如果数字是最后一个字符,则追加到新字符串。
        • 如果数字介于两者之间,请检查最后附加的字符是否为空格。如果没有空格,请输入一个空格,然后输入数字。
        • 如果最后插入的字符是数字,并且这也是数字,则插入。
        • 但是,如果 last 是数字,但它不是数字(也不是空格),则插入空格。

        您可能需要进一步调整它。

        如果字符串是这样的:

        "enter 144    code   here    123    "
        

        ?

        【讨论】:

        • 对你的问题:我想我可以稍后通过另一个函数删除重复的空格。应该很容易。也许这甚至没有必要。基本问题只是输入没有足够的空间。很多空间目前不是问题。
        猜你喜欢
        • 2016-11-02
        • 1970-01-01
        • 2021-06-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多