C++ 用空格和引号标记字符串

问题描述 投票:0回答:8

我想用 C++ 写一些东西来标记字符串。为了清楚起见,请考虑以下字符串:

add string "this is a string with spaces!"

必须按如下方式拆分:

add
string
this is a string with spaces!

有没有一种快速且基于标准库的方法?

c++ string token
8个回答
10
投票

不需要图书馆。迭代可以完成任务(如果它像您描述的那么简单)。

string str = "add string \"this is a string with space!\"";

for( size_t i=0; i<str.length(); i++){

    char c = str[i];
    if( c == ' ' ){
        cout << endl;
    }else if(c == '\"' ){
        i++;
        while( str[i] != '\"' ){ cout << str[i]; i++; }
    }else{
        cout << c;
    }
}

输出

add
string
this is a string with space!

7
投票

我想知道为什么这里没有介绍这个简单的 C++ 风格的解决方案。 它基于这样的事实:如果我们首先用

\"
分割字符串,那么每个偶数块都是“内部”引号,并且每个奇数块应该另外用空格分割。

不可能出现超出范围或其他任何情况。

unsigned counter = 0;
std::string segment;
std::stringstream stream_input(input);
while(std::getline(stream_input, segment, '\"'))
{
    ++counter;
    if (counter % 2 == 0)
    {
        if (!segment.empty())
            std::cout << segment << std::endl;
    }
    else
    {
        std::stringstream stream_segment(segment);
        while(std::getline(stream_segment, segment, ' '))
            if (!segment.empty())
                std::cout << segment << std::endl;
    }
}

5
投票

这是它的完整功能。根据需要修改它,它将部分字符串添加到向量字符串中(

qargs
)。

void split_in_args(std::vector<std::string>& qargs, std::string command){
        int len = command.length();
        bool qot = false, sqot = false;
        int arglen;
        for(int i = 0; i < len; i++) {
                int start = i;
                if(command[i] == '\"') {
                        qot = true;
                }
                else if(command[i] == '\'') sqot = true;

                if(qot) {
                        i++;
                        start++;
                        while(i<len && command[i] != '\"')
                                i++;
                        if(i<len)
                                qot = false;
                        arglen = i-start;
                        i++;
                }
                else if(sqot) {
                        i++;
                        start++;
                        while(i<len && command[i] != '\'')
                                i++;
                        if(i<len)
                                sqot = false;
                        arglen = i-start;
                        i++;
                }
                else{
                        while(i<len && command[i]!=' ')
                                i++;
                        arglen = i-start;
                }
                qargs.push_back(command.substr(start, arglen));
        }
        for(int i=0;i<qargs.size();i++){
                std::cout<<qargs[i]<<std::endl;
        }
        std::cout<<qargs.size();
        if(qot || sqot) std::cout<<"One of the quotes is open\n";
}

3
投票

Boost 库有一个

tokenizer
类,可以接受
escaped_list_separator
。这些的组合看起来可能会提供您正在寻找的东西。

这里是 boost 文档的链接,截至本文为止是最新版本,并且在您阅读本文时几乎可以肯定是旧版本。

https://www.boost.org/doc/libs/1_73_0/libs/tokenizer/doc/tokenizer.htm

https://www.boost.org/doc/libs/1_73_0/libs/tokenizer/doc/escaped_list_separator.htm

这个例子是从 boost 文档中盗来的。请原谅我没有创建自己的示例。

// simple_example_2.cpp
#include<iostream>
#include<boost/tokenizer.hpp>
#include<string>

int main(){
   using namespace std;
   using namespace boost;
   string s = "Field 1,\"putting quotes around fields, allows commas\",Field 3";
   tokenizer<escaped_list_separator<char> > tok(s);
   for(tokenizer<escaped_list_separator<char> >::iterator beg=tok.begin(); beg!=tok.end();++beg){
       cout << *beg << "\n";
   }
}

2
投票

C++14 或更高版本中有基于标准库的方法。但这并不快。

#include <iomanip> // quoted
#include <iostream>
#include <sstream> // stringstream
#include <string>

using namespace std;

int main(int argc, char **argv) {
    string str = "add string \"this is a string with spaces!\"";
    stringstream ss(str);
    string word;
    while (ss >> quoted(word)) {
        cout << word << endl;
    }
    return 0;
}

1
投票

我会定义一个类

Token
来从流中读取单个令牌。

然后使用你的代码就变得非常简单了。

#include <iostream>
#include <string>

int main()
{
    // Simply read the tokens from the stream.
    Token   t;
    while(std::cin >> t)
    {
        std::cout << "Got: " << t << "\n";
    }
}

像这样的 Stream 对象非常容易编写:

class Token
{
    // Just something to store the value in.
    std::string     value;

    // Then define the input and output operators.
    friend std::ostream& operator<<(std::ostream& str, Token const& output)
    {
        return str << output.value;
    }

    // Input is slightly harder than output.
    // but not that difficult to get correct.
    friend std::istream& operator>>(std::istream& str, Token& input)
    {
        std::string tmp;
        if (str >> tmp)
        {
            if (tmp[0] != '"')
            {
                // We read a word that did not start with
                // a quote mark. So we are done. Simply put
                // it in the destination.
                input.value = std::move(tmp);
            }
            else if (tmp.front() == '"' && tmp.back() == '"')
            {
                // we read a word with both open and close
                // braces so just nock these off.
                input.value = tmp.substr(1, tmp.size() - 2);
            }
            else
            {
                // We read a word that has but has a quote at the
                // start. So need to get all the characters upt
                // closing quote then add this to value.
                std::string tail;
                if (std::getline(str, tail, '"'))
                {
                    // Everything worked
                    // update the input
                    input.value = tmp.substr(1) + tail;
                }
            }
        }
        return str;
    }
};

0
投票

这是我的解决方案,它相当于python的shlex,shlex_join()是shlex_split()的逆:

#include <cctype>
#include <iomanip>
#include <iostream>
#include <string>
#include <sstream>
#include <utility>
#include <vector>

// Splits the given string using POSIX shell-like syntax.
std::vector<std::string> shlex_split(const std::string& s)
{
  std::vector<std::string> result;

  std::string token;
  char quote{};
  bool escape{false};

  for (char c : s)
  {
    if (escape)
    {
      escape = false;
      if (quote && c != '\\' && c != quote)
        token += '\\';
      token += c;
    }
    else if (c == '\\')
    {
      escape = true;
    }
    else if (!quote && (c == '\'' || c == '\"'))
    {
      quote = c;
    }
    else if (quote && c == quote)
    {
      quote = '\0';
      if (token.empty())
        result.emplace_back();
    }
    else if (!isspace(c) || quote)
    {
      token += c;
    }
    else if (!token.empty())
    {
      result.push_back(std::move(token));
      token.clear();
    }
  }

  if (!token.empty())
  {
    result.push_back(std::move(token));
    token.clear();
  }

  return result;
}

// Concatenates the given token list into a string. This function is the
// inverse of shlex_split().
std::string shlex_join(const std::vector<std::string>& tokens)
{
  auto it = tokens.begin();
  if (it == tokens.end())
    return {};

  std::ostringstream oss;
  while (true)
  {
    if (it->empty() || it->find_first_of(R"( "\)") != std::string::npos)
      oss << std::quoted(*it);
    else
      oss << *it;

    if (++it != tokens.end())
      oss << ' ';
    else
      break;
  }
  return oss.str();
}

void test(const std::string& s, const char* expected = nullptr)
{
  if (!expected)
    expected = s.c_str();
  if (auto r = shlex_join(shlex_split(s)); r != expected)
    std::cerr << '[' << s << "] -> [" << r << "], expected [" << expected << "]\n";
}

int main()
{
  test("");
  test(" ", "");
  test("a");
  test(" a ", "a");
  test("a   b", "a b");
  test(R"(a \s b)", "a s b");
  test(R"("a a" b)");
  test(R"('a a' b)", R"("a a" b)");
  test(R"(a \" b)", R"(a "\"" b)");
  test(R"(a \\ b)", R"(a "\\" b)");

  test(R"("a \" a" b)");
  test(R"('a \' a' b)", R"("a ' a" b)");
  test(R"("a \\ a" b)");
  test(R"('a \\ a' b)", R"("a \\ a" b)");
  test(R"('a \s a' b)", R"("a \\s a" b)");
  test(R"("a \s a" b)", R"("a \\s a" b)");
  test(R"('a \" a' b)", R"("a \\\" a" b)");
  test(R"("a \' a" b)", R"("a \\' a" b)");

  test(R"("" a)");
  test(R"('' a)", R"("" a)");
  test(R"(a "")");
  test(R"(a '')", R"(a "")");
}

0
投票

我想标准库没有直接的方法。间接跟随算法将起作用:

a) 使用 string::find('\"')` 搜索 '\"' 。如果找到任何内容,请使用 `string::find('\'',prevIndex)` 搜索下一个 '\"',如果找到,请使用 `string::substr()`。从原始字符串中丢弃该部分。

b) 现在以相同的方式搜索 '' '` 字符。

注意:您必须迭代整个字符串。

© www.soinside.com 2019 - 2024. All rights reserved.