簡體   English   中英

在 C++ 中解析逗號分隔的整數/整數范圍

[英]Parse comma-separated ints/int-ranges in C++

給定 C++ 中的字符串,其中包含范圍和單個數字:

"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 ) );

問題是它不適用於范圍。 它只取字符串中的數字,而不是范圍內的所有數字。

您的問題由兩個獨立的問題組成:

  1. 在 處將字符串拆分為多個字符串,
  2. 解析每個字符串時將數字或數字范圍添加到向量

如果你先用逗號分割整個字符串,你就不必擔心同時用連字符分割它。 這就是你所說的分而治之的方法。

分裂在,

這個問題應該告訴你如何用逗號分割字符串。

解析和添加到std::vector<int>

一旦你用逗號分割字符串,你只需要通過為每個字符串調用這個 function 來將范圍轉換為單獨的數字:

#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是最簡單的方法。 請注意,如果 integer 太大而無法放入int ,則std::stoi可能會引發異常。

除了@J。 Schultke 的優秀示例,我建議通過以下方式使用正則表達式:

#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;
}

當然,你仍然需要一點點驗證,但是,這會讓你開始。

到目前為止所有非常好的解決方案。 使用現代 C++ 和正則表達式,您只需很少幾行代碼即可完成一體化解決方案。

如何? 首先,我們定義一個匹配 integer 或 integer 范圍的正則表達式。 它看起來像這樣

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

真的很簡單。 先說范圍。 所以,一些數字,后跟一個連字符和更多數字。 然后是普通的 integer:一些數字。 所有數字都分組。 (大括號)。 連字符不在匹配組中。

這一切都如此簡單,無需進一步解釋。

然后我們在循環中調用std::regex_search ,直到找到所有匹配項。

對於每個匹配,我們檢查是否有子匹配,即范圍。 如果我們有子匹配,一個范圍,那么我們將子匹配之間的值(包括)添加到生成的std::vector中。

如果我們只有一個普通的 integer,那么我們只添加這個值。

所有這些都提供了一個非常簡單易懂的程序:

#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 社區版編譯和測試

考慮預處理您的數字字符串並將它們拆分。 在下面的代碼中, transform()會將分隔符之一、 , -+轉換為空格,以便std::istream_iterator成功解析 int。

#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 答案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM