C ++ में पार्स अल्पविराम से अलग किए गए इंटे / इंट-रेंज

Aug 17 2020

सी ++ में एक स्ट्रिंग को देखते हुए रेंज और तरह की एकल संख्याएं:

"2,3,4,7-9"

मैं इसे फॉर्म के वेक्टर में पार्स करना चाहता हूं:

2,3,4,7,8,9

यदि संख्याओं को अलग किया जाता है -तो मैं सभी संख्याओं को श्रेणी में धकेलना चाहता हूं। वरना मैं सिंगल नंबर को पुश करना चाहता हूं।

मैंने कोड के इस टुकड़े का उपयोग करने की कोशिश की:

const char *NumX = "2,3,4-7";
std::vector<int> inputs;
std::istringstream in( NumX );
std::copy( std::istream_iterator<int>( in ), std::istream_iterator<int>(),
           std::back_inserter( inputs ) );

समस्या यह थी कि यह सीमाओं के लिए काम नहीं करता था। इसने नंबरों को केवल स्ट्रिंग में लिया, न कि सभी नंबरों की रेंज में।

जवाब

3 d4rk4ng31 Aug 17 2020 at 08:32

इसके अलावा @ जे। शुल्त्स का उत्कृष्ट उदाहरण, मैं निम्नलिखित तरीकों से रेगीक्स के उपयोग का सुझाव देता हूं:

#include <algorithm>
#include <iostream>
#include <regex>
#include <string>
#include <vector>

void process(std::string str, std::vector<int>& num_vec) {
    str.erase(--str.end());
    for (int i = str.front() - '0'; i <= str.back() - '0'; i++) {
        num_vec.push_back(i);                                                     
    }
}

int main() {
    std::string str("1,2,3,5-6,7,8");
    str += "#";
    std::regex vec_of_blocks(".*?\,|.*?\#");
    auto blocks_begin = std::sregex_iterator(str.begin(), str.end(), vec_of_blocks);
    auto blocks_end = std::sregex_iterator();
    std::vector<int> vec_of_numbers;
    for (std::sregex_iterator regex_it = blocks_begin; regex_it != blocks_end; regex_it++) {
        std::smatch match = *regex_it;
        std::string block = match.str();
        if (std::find(block.begin(), block.end(), '-') != block.end()) {
            process(block, vec_of_numbers);
        }
        else {
            vec_of_numbers.push_back(std::atoi(block.c_str()));
        }
    }
    return 0;
}

बेशक, आपको अभी भी टैड बिट सत्यापन की आवश्यकता है, हालांकि, यह आपको शुरू कर देगा।

5 JanSchultke Aug 17 2020 at 07:54

आपकी समस्या में दो अलग-अलग समस्याएं हैं:

  1. स्ट्रिंग को कई स्ट्रिंग्स में विभाजित करना ,
  2. प्रत्येक स्ट्रिंग को पार्स करते समय संख्याओं या संख्याओं की श्रेणियों को वेक्टर में जोड़ना

यदि आप पहली बार पूरे स्ट्रिंग को अल्पविराम में विभाजित करते हैं, तो आपको एक ही समय में इसे एक हाइफ़न में विभाजित करने के बारे में चिंता करने की आवश्यकता नहीं होगी। यह वह है जिसे आप डिवाइड-एंड-कॉनकेयर दृष्टिकोण कहेंगे ।

पर बंट रहा है ,

यह प्रश्न आपको यह बताना चाहिए कि आप स्ट्रिंग को अल्पविराम में कैसे विभाजित कर सकते हैं।

पार्स करना और जोड़ना std::vector<int>

एक बार जब आप एक अल्पविराम पर स्ट्रिंग को विभाजित करते हैं, तो आपको प्रत्येक स्ट्रिंग के लिए इस फ़ंक्शन को कॉल करके अलग-अलग संख्याओं में बदलना होगा:

#include <vector>
#include <string>

void push_range_or_number(const std::string &str, std::vector<int> &out) {
    size_t hyphen_index;
    // stoi will store the index of the first non-digit in hyphen_index.
    int first = std::stoi(str, &hyphen_index);
    out.push_back(first);

    // If the hyphen_index is the equal to the length of the string,
    // there is no other number.
    // Otherwise, we parse the second number here:
    if (hyphen_index != str.size()) {
        int second = std::stoi(str.substr(hyphen_index + 1), &hyphen_index);
        for (int i = first + 1; i <= second; ++i) {
            out.push_back(i);
        }
    }
}

ध्यान दें कि एक हाइफ़न में विभाजित करना बहुत सरल है क्योंकि हम जानते हैं कि स्ट्रिंग में अधिकतम एक हाइफ़न हो सकता है। std::string::substrइस मामले में इसे करने का सबसे आसान तरीका है। यह जान लें कि std::stoiयदि पूर्णांक एक में फिट होने के लिए बहुत बड़ा है तो अपवाद को फेंक सकते हैं int

2 ArminMontigny Aug 17 2020 at 15:03

अब तक के सभी बहुत अच्छे समाधान। आधुनिक C ++ और regex का उपयोग करके, आप कोड की केवल कुछ पंक्तियों के साथ एक ऑल-इन-वन समाधान कर सकते हैं।

कैसे? सबसे पहले, हम एक रेगेक्स को परिभाषित करते हैं जो या तो पूर्णांक या पूर्णांक श्रेणी से मेल खाता है। यह इस तरह दिखेगा

((\d+)-(\d+))|(\d+)

वास्तव में बहुत सरल है। पहले सीमा। तो, कुछ अंक, उसके बाद एक हाइफ़न और कुछ और अंक। फिर सादा पूर्णांक: कुछ अंक। सभी अंक समूहों में रखे जाते हैं। (ब्रेसिज़)। हाइफ़न एक मिलान समूह में नहीं है।

यह सब इतना आसान है कि किसी और स्पष्टीकरण की आवश्यकता नहीं है।

तब हम std::regex_searchएक लूप में कॉल करते हैं , जब तक कि सभी मैच नहीं मिलते।

प्रत्येक मैच के लिए, हम जाँचते हैं, यदि उप-मैच हैं, जिसका अर्थ है एक सीमा। यदि हमारे पास उप-मैच हैं, एक सीमा है, तो हम परिणाम के लिए उप-मैचों (समावेशी) के बीच मूल्यों को जोड़ते हैं std::vector

यदि हमारे पास केवल एक पूर्णांक है, तो हम केवल इस मान को जोड़ते हैं।

यह सब एक बहुत ही सरल और समझने में आसान कार्यक्रम देता है:

#include <iostream>
#include <string>
#include <vector>
#include <regex>

const std::string test{ "2,3,4,7-9" };

const std::regex re{ R"(((\d+)-(\d+))|(\d+))" };
std::smatch sm{};

int main() {
    // Here we will store the resulting data
    std::vector<int> data{};

    // Search all occureences of integers OR ranges
    for (std::string s{ test }; std::regex_search(s, sm, re); s = sm.suffix()) {

        // We found something. Was it a range?
        if (sm[1].str().length())

            // Yes, range, add all values within to the vector  
            for (int i{ std::stoi(sm[2]) }; i <= std::stoi(sm[3]); ++i) data.push_back(i);
        else
            // No, no range, just a plain integer value. Add it to the vector
            data.push_back(std::stoi(sm[0]));
    }
    // Show result
    for (const int i : data) std::cout << i << '\n';
    return 0;
}

यदि आपके पास अधिक प्रश्न हैं, तो मुझे उत्तर देने में खुशी होगी।


भाषा: C ++ 17 का संकलन और MS Visual Studio 19 सामुदायिक संस्करण के साथ परीक्षण किया गया

JohnPark Aug 17 2020 at 08:42

अपनी संख्या स्ट्रिंग की पूर्व-प्रक्रिया पर विचार करें और उन्हें विभाजित करें। निम्नलिखित कोड में, transform()डेलिम्स में से एक को , , -और +एक स्थान में परिवर्तित करेगा ताकि std::istream_iteratorपार्स इंट सफलतापूर्वक हो।

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

int main(void)
{
    std::string nums = "2,3,4-7,9+10";
    const std::string delim_to_convert = ",-+";  // , - and +
    std::transform(nums.cbegin(), nums.cend(), nums.begin(),
            [&delim_to_convert](char ch) {return (delim_to_convert.find(ch) != string::npos) ? ' ' : ch; });

    std::istringstream ss(nums);
    auto inputs = std::vector<int>(std::istream_iterator<int>(ss), {});

    exit(EXIT_SUCCESS);
}

ध्यान दें कि ऊपर दिया गया कोड केवल 1-बाइट लंबाई में विभाजित हो सकता है। यदि आपको अधिक जटिल और लंबे समय तक देरी की आवश्यकता है, तो आपको @ d4rk4ng31 उत्तर का उल्लेख करना चाहिए।