แยกวิเคราะห์ ints / int-range ที่คั่นด้วยจุลภาคใน 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 ) );
ปัญหาคือมันใช้ไม่ได้กับช่วง ใช้เฉพาะตัวเลขในสตริงไม่ใช่ตัวเลขทั้งหมดในช่วง
คำตอบ
นอกเหนือจาก @J. ตัวอย่างที่ยอดเยี่ยมของ Schultke ฉันขอแนะนำให้ใช้ regexes ด้วยวิธีต่อไปนี้:
#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;
}
แน่นอนคุณยังต้องมีการตรวจสอบความถูกต้องเล็กน้อยอย่างไรก็ตามสิ่งนี้จะช่วยให้คุณเริ่มต้นได้
ปัญหาของคุณประกอบด้วยสองปัญหาที่แยกจากกัน:
- การแยกสตริงออกเป็นหลาย ๆ สตริงที่
,
- การเพิ่มตัวเลขหรือช่วงของตัวเลขลงในเวกเตอร์เมื่อแยกวิเคราะห์แต่ละสตริง
หากคุณแยกสตริงทั้งหมดโดยใช้เครื่องหมายจุลภาคเป็นครั้งแรกคุณจะไม่ต้องกังวลกับการแยกสตริงด้วยยัติภังค์ในเวลาเดียวกัน นี่คือสิ่งที่คุณจะเรียกหารและพิชิตวิธี
แยกที่ ,
คำถามนี้ควรบอกคุณว่าคุณสามารถแยกสตริงโดยใช้ลูกน้ำได้อย่างไร
การแยกวิเคราะห์และการเพิ่มลงใน 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 ++ และ regex ที่ทันสมัยคุณสามารถทำโซลูชันแบบออล - อิน - วันโดยใช้โค้ดเพียงไม่กี่บรรทัด
อย่างไร? ขั้นแรกเรากำหนด 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 Community Edition
พิจารณาประมวลผลสตริงตัวเลขของคุณล่วงหน้าและแยกออก ในโค้ดต่อไปนี้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 หากคุณต้องการตัวคั่นที่ซับซ้อนและยาวกว่านี้