2017-05-23 84 views
0

我有这个格式的字符串:我怎样才能提取值对从C++字符串

"name1":1234 " name2 " : 23456 "name3" : 12345 

等等...

我曾尝试使用嵌套while循环和两个整数存储在string::substr中使用的位置和长度,但我找不到一个合适的方式来获取它(大多数时候我最终从字符串中结束)。

这些值不需要存储,因为我可以调用一个函数来处理它们,只要我得到它们。

这是我迄今所做的:提前

void SomeClass::processProducts(std::string str) { 
unsigned int i = 0; 
std::string name; 
    while (i < str.length()) { 
     if (str[i] == '\"') { 
      int j = 1; 
      while (str[i + j] != '\"') { 
       j++; 
      } 
      name = str.substr(i + 1, j - 1); 
      i += j; 
     } 
     else if (str[i] >= '0' && str[i] <= '9') { 
      int j = 1; 
      while (str[i + j] >= '0' && str[i + j] <= '9') { 
       j++; 
      } 

      //This is just processes the values 
      std::stringstream ss; 
      std::string num = str.substr(i, j); 
      ss.str(num); 
      int products = 0; 
      ss >> products; 
      if (products == 0) { 
       Util::error(ERR_WRONG_PRODUCTS); 
      } 
      int pos = getFieldPos(name); 
      if (pos == -1) { 
       Util::error(ERR_WRONG_NAME); 
      } 
      else { 
       fields[pos].addProducts(products); 
      } 
      i += j; 
     } 
     i++; 
    } 
} 

感谢。

+0

欢迎堆栈溢出。请花些时间阅读[The Tour](http://stackoverflow.com/tour),并参阅[帮助中心](http://stackoverflow.com/help/asking)中的资料,了解您可以在这里问。 –

+0

请[编辑]您的问题,以显示您尝试过的并不适合您的实际代码。到目前为止,你自己调试过什么? –

+0

你想得到什么结果?例如,你想从“name1”中得到什么? –

回答

2

不幸的是,C++没有强大的字符串解析功能。这就是为什么有很多方法来完成这些任务。

但是,C++确实提供了帮助的工具。所以我们可以使用它们,至少避免手动循环。

在我们开始之前,我想提请注意的事实是,当我们处理用户输入时,我们必须格外小心地验证输入。

,我们需要为我选择的解决方案的模块包括:

  • 匹配的格式(与"name" : value)。为此,我选择了std::find。正则表达式也可以使用。
  • value解析为数字。为此,我们可以使用std::stoi。见吼声为什么它是不够的。
  • 总是确保我们得到我们期望的输入。这增加了一些样板代码,但这是我们必须支付的价格。同样在这里,我们遇到std::stoi问题,因为它很高兴地接受非空白字符而没有大惊小怪。因此,例如123 invalid将被解析为123。这就是我之所以用小包装它周围parse_string_to_int

好,在我们去:

小帮手:

auto parse_string_to_int(const std::string& str) 
{ 
    std::size_t num_processed = 0; 
    int val     = std::stoi(str, &num_processed, 10); 

    auto next_non_space = std::find_if(str.begin() + num_processed, str.end(), 
             [](char ch) { return !std::isspace(ch); }); 

    if (next_non_space != str.end()) 
     throw std::invalid_argument{"extra trailing characters in parse_string_to_int"}; 

    return val; 
} 
struct Product_token 
{ 
    std::string name; 
    int value; 
}; 

auto get_next_product(std::string::const_iterator& begin, std::string::const_iterator end) 
    -> Product_token 
{ 
    // match `"name" : value "` 
    auto name_open_quote  = std::find(begin, end, '\"'); 
    auto name_close_quote  = std::find(name_open_quote + 1, end, '\"'); 
    auto colon     = std::find(name_close_quote, end, ':'); 
    auto next_token_open_quote = std::find(colon, end, '\"'); 

    if (name_close_quote == end || name_close_quote == end || colon == end) 
    { 
     // feel free to add more information regarding the error. 
     // this is just the bare minimum to accept/reject the input 
     throw std::invalid_argument{"syntax error on parsing product"}; 
    } 

    // advance to next token 
    begin = next_token_open_quote; 

    return Product_token{{name_open_quote + 1, name_close_quote}, 
         parse_string_to_int({colon + 1, next_token_open_quote})}; 
} 

auto process_products(const std::string& str) 
{ 
    auto begin = str.begin(); 

    while (begin != str.end()) 
    { 
     auto product = get_next_product(begin, str.end()); 
     cout << '"' << product.name << "\" = " << product.value << endl; 
    } 
} 
int main() 
{ 
    auto str = R"("name1":1234 " name2 " : 23456 "name3" : 12345)"s; 

    try 
    { 
     process_products(str); 
    } 
    catch (std::exception& e) 
    { 
     cerr << e.what() << endl; 
    } 
} 

查看完整的代码在行动on ideone

+0

看起来很棒!非常感谢。 –

+0

刚刚实施它,像魅力 –

+0

@PabloRamónGuevara伟大。考虑将答案upvoting并标记为已接受 – bolov

0

只要您知道格式,然后提取数据是相当容易的。首先删除字符串中的任何引号或冒号,并用空格替换它们。现在字符串由空格分隔。

#include <iostream>                                               
#include <iterator> 
#include <string> 
#include <algorithm> 
#include <vector> 
#include <sstream> 

using namespace std; 


int main() 
{ 
    string str("\"name1\":1234 \" name2 \" : 23456 \"name3\" : 12345"); 
    cout << str << endl; 
    // remove ':' and '"' and replace them by space 
    std::replace_if(str.begin(), str.end(), ispunct, ' '); 
    istringstream ss(str); 
    vector<string> words; 
    // store data as name and number in vector<string> 
    copy(istream_iterator<string>(ss),istream_iterator<string>(),back_inserter(words)); 

    for (int i(0); i < words.size(); i+=2) 
     cout << "name: " << words[i] << " number: " << words[i+1] << endl; 


    return 0; 
} 

结果是

"name1":1234 " name2 " : 23456 "name3" : 12345 
name: name1 number: 1234 
name: name2 number: 23456 
name: name3 number: 12345 
+0

好的答案,但由于该名称可能包含空格,因此它不适用于我的请求。 –