Parse ints / int-range yang dipisahkan koma di C ++
Diberikan string dalam C ++ yang berisi rentang dan nomor tunggal dari jenis:
"2,3,4,7-9"
Saya ingin menguraikannya menjadi vektor bentuk:
2,3,4,7,8,9
Jika angka dipisahkan oleh -
maka saya ingin mendorong semua angka dalam kisaran tersebut. Kalau tidak, saya ingin mendorong satu nomor.
Saya mencoba menggunakan kode ini:
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 ) );
Masalahnya adalah itu tidak berfungsi untuk rentang. Hanya butuh angka dalam string, tidak semua angka dalam range.
Jawaban
Selain @J. Contoh bagus Schultke, saya menyarankan penggunaan regex dengan cara berikut:
#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;
}
Tentu saja, Anda masih memerlukan sedikit validasi, namun ini akan membantu Anda memulai.
Masalah Anda terdiri dari dua masalah terpisah:
- membagi string menjadi beberapa string di
,
- menambahkan angka atau rentang angka ke vektor saat mengurai setiap string
Jika Anda pertama kali memisahkan seluruh string dengan tanda koma, Anda tidak perlu khawatir tentang memisahkannya dengan tanda hubung pada saat yang bersamaan. Inilah yang Anda sebut pendekatan Divide-and-Conquer .
Membelah di ,
Pertanyaan ini akan memberi tahu Anda bagaimana Anda bisa membagi string dengan koma.
Parsing dan Menambahkan ke std::vector<int>
Setelah Anda membagi string menjadi koma, Anda hanya perlu mengubah rentang menjadi angka individual dengan memanggil fungsi ini untuk setiap string:
#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);
}
}
}
Perhatikan bahwa memisahkan tanda hubung jauh lebih sederhana karena kita tahu paling banyak hanya ada satu tanda hubung dalam string. std::string::substradalah cara termudah untuk melakukannya dalam kasus ini. Ketahuilah bahwa std::stoidapat memunculkan pengecualian jika bilangan bulat terlalu besar untuk dimasukkan ke dalam file int
.
Semua solusi yang sangat bagus sejauh ini. Dengan menggunakan C ++ dan regex modern, Anda dapat melakukan solusi all-in-one hanya dengan beberapa baris kode.
Bagaimana? Pertama, kami mendefinisikan regex yang cocok dengan integer ATAU rentang integer. Ini akan terlihat seperti ini
((\d+)-(\d+))|(\d+)
Sangat sangat sederhana. Pertama rentangnya. Jadi, beberapa digit, diikuti dengan tanda hubung dan beberapa digit lagi. Kemudian bilangan bulat biasa: Beberapa digit. Semua digit dimasukkan ke dalam kelompok. (kawat gigi). Tanda hubung tidak ada dalam grup yang cocok.
Ini semua sangat mudah sehingga tidak perlu penjelasan lebih lanjut.
Kemudian kita memanggil std::regex_search
dalam satu lingkaran, sampai semua kecocokan ditemukan.
Untuk setiap kecocokan, kami periksa, jika ada sub-kecocokan, yang berarti rentang. Jika kita memiliki sub-kecocokan, sebuah range, maka kita menambahkan nilai-nilai antara sub-kecocokan (inklusif) ke hasil std::vector
.
Jika kita hanya memiliki integer biasa, maka kita hanya menambahkan nilai ini.
Semua ini memberikan program yang sangat sederhana dan mudah dimengerti:
#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;
}
Jika Anda memiliki lebih banyak pertanyaan, saya dengan senang hati menjawabnya.
Bahasa: C ++ 17 Dikompilasi dan diuji dengan MS Visual Studio 19 Community Edition
Pertimbangkan untuk memproses terlebih dahulu string nomor Anda dan pisahkan. Dalam kode berikut, transform()
akan mengubah salah satu pembatas, ,
-
dan +
, menjadi spasi sehingga std::istream_iterator
parse int berhasil.
#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);
}
Perhatikan bahwa kode di atas hanya dapat memisahkan pembatas dengan panjang 1 byte. Anda harus merujuk ke jawaban @ d4rk4ng31 jika Anda membutuhkan pembatas yang lebih kompleks dan lebih panjang.