2017-05-23 1 views
0

C의 문자열 ++에서 값의 쌍을 추출 할 수 있습니다 : 나는 동안 중첩 사용하여 시도했다어떻게 내가이 형식의 문자열이

...

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

등을 루프와 두 개의 정수 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)를 읽으신 후 [Help Center] (http://stackoverflow.com/help/asking)의 자료를 참조하십시오. 여기에 물어보십시오. –

+0

질문을 편집하여 실제 작동하지 않는 코드를 표시하십시오. 지금까지 직접 디버깅하기 위해 무엇을 했습니까? –

+0

어떤 결과를 원하십니까? 예를 들어 "name1"에서 무엇을 원하겠습니까? –

답변

2

불행히도 C++에는 강력한 문자열 구문 분석 기능이 기본적으로 제공되지 않습니다. 그래서 이러한 종류의 작업을 수행하는 방법은 다양합니다.

그러나 C++에서는 도움이되는 도구를 제공합니다. 그래서 우리는 그것들을 사용할 수 있고 수동 루프를 피할 수 있습니다.

시작하기 전에 사용자 입력을 처리 할 때 입력 유효성을 검사하는 데 특별한주의를 기울여야한다는 사실에 주목하고 싶습니다. 우리는 내가 선택한 솔루션에 필요한

블록은 다음과 같습니다 ("name" : value 포함) 형식을 일치

  • . 이를 위해 std::find을 선택했습니다. 정규식을 사용할 수도 있습니다.
  • 숫자로 value을 구문 분석합니다. 이를 위해 std::stoi을 사용할 수 있습니다. 그것이 충분하지 않은 이유를보십시오.
  • 항상 우리가 기대하는 정보를 얻고 있는지 확인하십시오. 일부 상용구 코드이 추가되었지만 이는 우리가 지불해야하는 가격입니다. 또한 여기에 우리는 std::stoi에 문제가 있습니다. 행복하게 공백없는 공백을 허용하기 때문입니다. 예를 들어 123 invalid123으로 구문 분석됩니다. 이것은 우리가 가야에 나는 주위 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 및 marking 대답을 받아 들인 것으로 간주하십시오. – 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

좋은 답변이지만 이름에 공백이있을 수 있기 때문에 요청할 때 작동하지 않습니다. –