2017-05-23 128 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

好的答案,但由於該名稱可能包含空格,因此它不適用於我的請求。 –