C ++에서 쉼표로 구분 된 ints / int-ranges 구문 분석
범위와 종류의 단일 숫자를 포함하는 C ++의 문자열이 제공됩니다.
"2,3,4,7-9"
다음과 같은 형식의 벡터로 구문 분석하고 싶습니다.
2,3,4,7,8,9
숫자가 a -
로 구분 되면 범위의 모든 숫자를 밀어 넣고 싶습니다. 그렇지 않으면 하나의 숫자를 밀고 싶습니다.
이 코드를 사용해 보았습니다.
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 ) );
문제는 범위에서 작동하지 않는다는 것입니다. 범위의 모든 숫자가 아니라 문자열의 숫자 만 취했습니다.
답변
@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;
}
물론, 여전히 약간의 유효성 검사가 필요하지만 시작하면됩니다.
문제는 두 가지 개별 문제로 구성됩니다.
- 문자열을 여러 문자열로 분할
,
- 각 문자열을 구문 분석 할 때 벡터에 숫자 또는 숫자 범위 추가
처음에 전체 문자열을 쉼표로 분할하는 경우 동시에 하이픈으로 분할하는 것에 대해 걱정할 필요가 없습니다. 이것이 바로 Divide-and-Conquer 접근 방식입니다.
분할 ,
이 질문 은 쉼표로 문자열을 분할하는 방법을 알려줍니다.
구문 분석 및 추가 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
.
지금까지 모든 아주 좋은 솔루션. 최신 C ++ 및 정규식을 사용하면 몇 줄의 코드만으로 올인원 솔루션을 수행 할 수 있습니다.
어떻게? 먼저 정수 또는 정수 범위와 일치하는 정규식을 정의합니다. 이렇게 보일 것입니다
((\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 Community Edition으로 컴파일 및 테스트 됨
숫자 문자열을 사전 처리하고 분할하는 것을 고려하십시오. 다음 코드에서 transform()
delims 중 하나를 변환 할 ,
-
및 +
그래서 공간으로, 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 바이트 길이의 delim 만 분할 할 수 있습니다. 더 복잡하고 더 긴 delims가 필요한 경우 @ d4rk4ng31 답변을 참조해야합니다.