Второе задание
Файлы выписки плана нумерации ФАС имеют CSV-формат. Столбцы:
Код оператора: 3 цифры;
Стартовый номер абонента: 7 цифр;
Конечный номер абонента: 7 цифр;
Число номеров в диапазоне: целое число;
Оператор: строка;
Регион: строка.
Пример строки файла: 301;2110000;2129999;20000;ПАО "Ростелеком";г. Улан-Удэ|Республика Бурятия.
Два наиболее производительных решения (по поиску диапазона по мобильному номеру):
Решение с двусторонней очередью и бинарным поиском, поиск за
,
где
— число диапазонов (результат тестирования
производительности представлен на
рисунке 2).Решение с деревом цифр и минимальным масками диапазонов, поиск за
,
где
— количество цифр номера телефона
(результат тестирования производительности
представлен на рисунке 3).
Рисунок 2. Тестирование версии с двусторонней очередью и бинарным поиском
Рисунок 3. Тестирование версии с деревом цифр и минимальным масками диапазонов
Without check results — без проверки номера телефона на корректность.
With check results — с проверкой номера телефона на корректность.
Решение с двусторонней очередью и бинарным поиском состоит из 10 файлов, представленных в таблицах 11-20.
Таблица 11. Файл main.cpp
main.cpp |
#include <string> #include <iostream> #include <fstream> #include <filesystem> #include <stdexcept> #include "lib/mobile_numbers_data.h"
namespace fs = std::filesystem;
std::vector<fs::path> get_files_in_directory(const std::wstring &directory, std::wstring ext) { std::vector<fs::path> files_vector; for (const auto &file : fs::directory_iterator(directory)) { fs::path filepath = file.path(); if (fs::is_regular_file(filepath) && (ext.empty() || filepath.extension() == ext)) { files_vector.emplace_back(filepath); } } return files_vector; }
std::wstring replace(const std::wstring &str, const std::wstring &substr, const std::wstring &repl_substr) { size_t pos = str.find(substr); if (pos != std::wstring::npos) { return str.substr(0, pos) + repl_substr + str.substr(pos + substr.size(), std::string::npos); } return str; }
int main(int argc, char *argv[]) { // set locale (required for wstring) std::locale::global(std::locale("C.UTF-8")); // check args if (argc != 2) { std::wcout << "Use: ./main {mobile_number}" << std::endl; return 1; } // check mobile number std::string mobile_number(argv[1]); if (!MobileNumbersData::is_mobile_number(mobile_number)) { std::wcout << "Invalid mobile number format." << std::endl; return 2; } // get csv files in current directory std::vector<fs::path> files_vector = get_files_in_directory(L"./", L".csv"); if (files_vector.empty()) { std::wcout << "CSV files not found." << std::endl; return 3; } try { MobileNumbersData mobile_numbers_data; for (const auto &file : files_vector) { std::wifstream wstream(file); if (!wstream.is_open()) { std::wcout << "File \"" << file.wstring() << "\" skipped (open error)." << std::endl; continue; } mobile_numbers_data.add_data(wstream); } auto range = mobile_numbers_data.find_with_check(mobile_number); auto mobile_operator = range.operator_name(); auto region = range.region(); region = replace(region, L";", L", "); region = replace(region, L"|", L", "); std::wcout << mobile_operator << ", " << region << std::endl; } catch (const std::exception &e) { std::wcout << e.what() << std::endl; return 4; } return 0; } |
Таблица 12. Файл test.cpp
test.cpp |
#include <string> #include <iostream> #include <fstream> #include <filesystem> #include <stdexcept> #include <chrono> #include "lib/mobile_numbers_data.h"
namespace fs = std::filesystem;
// Get the current time in seconds // This function should be used only for measuring time // (first call, algorithm, second call; then the difference between the values) #if defined(_WIN32) #include <Windows.h> double get_proc_time() { FILETIME createTime, exitTime, kernelTime, userTime; if (GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime) != -1) return (ULARGE_INTEGER{{userTime.dwLowDateTime, userTime.dwHighDateTime}}).QuadPart / 10000000.L; return 0; } #else double get_proc_time() { return static_cast<double>(clock()) / CLOCKS_PER_SEC; } #endif
std::vector<fs::path> get_files_in_directory(const std::wstring &directory, std::wstring ext) { std::vector<fs::path> files_vector; for (const auto &file : fs::directory_iterator(directory)) { fs::path filepath = file.path(); if (fs::is_regular_file(filepath) && (ext.empty() || filepath.extension() == ext)) { files_vector.emplace_back(filepath); } } return files_vector; }
std::wstring replace(const std::wstring &str, const std::wstring &substr_from, const std::wstring &substr_to) { size_t pos = str.find(substr_from); if (pos != std::wstring::npos) { return str.substr(0, pos) + substr_to + str.substr(pos + substr_from.size(), std::string::npos); } return str; }
int main(int argc, char *argv[]) { // set locale (required for wstring) std::locale::global(std::locale("C.UTF-8")); // check args if (argc != 2) { std::wcout << "Use: ./test {number of tests}" << std::endl; return 1; } int n_tests = 0; try { n_tests = std::atoi(argv[1]); if (n_tests <= 0) { throw std::invalid_argument("Arg {number of tests} is non-positive number."); } } catch (const std::exception &e) { std::wcout << e.what() << std::endl; return 2; } // find csv files in current directory std::vector<fs::path> files_vector = get_files_in_directory(L"./", L".csv"); if (files_vector.empty()) { std::wcout << "CSV files not found." << std::endl; return 3; } try { // start testing MobileNumbersData mobile_numbers_data; double s = 0, start, finish; for (const auto &file : files_vector) { std::wifstream wstream(file); if (!wstream.is_open()) { std::wcout << "File \"" << file.wstring() << "\" skipped (open error)." << std::endl; continue; } start = get_proc_time(); mobile_numbers_data.add_data(wstream); finish = get_proc_time(); s += finish - start; } std::wcout << "-- Adding data from files: " << s << " seconds" << std::endl; s = 0; for (int i = 0; i < n_tests; ++i) { auto mobile_number = mobile_numbers_data.generate(); start = get_proc_time(); auto range = mobile_numbers_data.find_without_check(mobile_number); finish = get_proc_time(); s += finish - start; } std::wcout << "== Without check results" << std::endl; std::wcout << "-- Testing time: " << s << " seconds" << std::endl; s = 0; for (int i = 0; i < n_tests; ++i) { auto mobile_number = mobile_numbers_data.generate(); start = get_proc_time(); auto range = mobile_numbers_data.find_with_check(mobile_number); finish = get_proc_time(); s += finish - start; } std::wcout << "== With check results" << std::endl; std::wcout << "-- Testing time: " << s << " seconds" << std::endl; } catch (const std::exception &e) { std::wcout << e.what() << std::endl; return 4; } return 0; } |
Таблица 13. Файл lib/mobile_number_defines.h
lib/mobile_number_defines.h |
#ifndef MOBILE_NUMBER_DEFINES_H #define MOBILE_NUMBER_DEFINES_H
#include <cmath> #include <string> #include <regex> #include <random> #include <functional>
namespace MobileNumberDefines { constexpr size_t COUNTRY_CODE_DIGITS = 1; constexpr size_t OPERATOR_CODE_DIGITS = 3; constexpr size_t CALLER_CODE_DIGITS = 7; constexpr int MAX_COUNTRY_CODE = std::pow(10, COUNTRY_CODE_DIGITS) - 1; constexpr int MAX_OPERATOR_CODE = std::pow(10, OPERATOR_CODE_DIGITS) - 1; constexpr int MAX_CALLER_CODE = std::pow(10, CALLER_CODE_DIGITS) - 1;
const std::regex mobile_number_regex("^(?:(\\+?\\d)[\\- ]?)?(?:\\(?(\\d{3})\\)?[\\- ]?)(\\d{3})[\\- ]?(\\d{2})[\\- ]?(\\d{2})$"); /* Can work with next mobile numbers +79811742698 +7(981)1742698 +7-(981)-174-26-98 89811742698 9811742698 (981) 174 2698 (981)-174-2698 981-174-2698 981 174 2698 9811742698 ... country_code -- group #1 operator_code -- group #2 caller_code -- groups #3, #4 and #5 */
const std::basic_regex<wchar_t> mobile_numbers_range_regex(L"^(\\d{3});(\\d{7});(\\d{7});[^;]*?;([^;]*);(.*)$"); /* Can work with next mobile numbers range АВС/ DEF;От;До;Емкость;Оператор;Регион 301;2110000;2129999;20000;ПАО "Ростелеком";г. Улан-Удэ|Республика Бурятия 301;2150000;2169999;20000;ПАО "Ростелеком";г. Улан-Удэ|Республика Бурятия 301;2180000;2189999;10000;ПАО "Ростелеком";г. Улан-Удэ|Республика Бурятия ... operator_code -- group #1 caller_code_from -- group #2 caller_code_to -- group #3 mobile_operator -- group #4 region -- group #5 */ }
namespace RandomGen { extern std::random_device random_device_mn; extern std::mt19937 mt; extern std::uniform_int_distribution<size_t> size_t_distribution; extern std::uniform_int_distribution<int> int_distribution; extern size_t get_random_size_t_number(size_t, size_t); extern int get_random_int_number(int, int); }
#endif // MOBILE_NUMBER_DEFINES_H
|
Таблица 14. Файл lib/mobile_number_defines.cpp
lib/mobile_number_defines.cpp |
#include "mobile_number_defines.h"
std::random_device RandomGen::random_device_mn;
std::mt19937 RandomGen::mt = std::mt19937(RandomGen::random_device_mn());
std::uniform_int_distribution<size_t> RandomGen::size_t_distribution(0, std::numeric_limits<size_t>::max());
std::uniform_int_distribution<int> RandomGen::int_distribution(0, std::numeric_limits<int>::max());
size_t RandomGen::get_random_size_t_number(size_t from, size_t to) { return from + RandomGen::size_t_distribution(RandomGen::mt) % (to - from + 1); }
int RandomGen::get_random_int_number(int from, int to) { return from + RandomGen::int_distribution(RandomGen::mt) % (to - from + 1); } |
Таблица 15. Файл lib/mobile_numbers_range.h
lib/mobile_numbers_range.h |
#ifndef MOBILE_NUMBERS_RANGE_H #define MOBILE_NUMBERS_RANGE_H
#include <list> #include <cmath> #include <string> #include <algorithm> #include <stdexcept> #include "mobile_number_defines.h"
class MobileNumbersRange { public: MobileNumbersRange(); MobileNumbersRange(std::string, std::string, std::string, std::wstring, std::wstring); MobileNumbersRange(int, int, int, std::wstring, std::wstring); // getters int operator_code_i() const; int caller_code_from_i() const; int caller_code_to_i() const; std::string operator_code() const; std::string caller_code_from() const; std::string caller_code_to() const; std::wstring operator_name() const; std::wstring region() const; // setters void set_operator_code_i(int); void set_caller_code_i(int, int); void set_operator_code(const std::string &); void set_operator_code(const std::wstring &); void set_caller_code(const std::string &, const std::string &); void set_caller_code(const std::wstring &, const std::wstring &); void set_operator_name(const std::wstring &); void set_region(const std::wstring &); // check int cmp_in_range(long) const; friend bool operator<(const MobileNumbersRange &, const MobileNumbersRange &);
private: int m_operator_code, m_caller_code_from, m_caller_code_to; std::wstring m_operator_name, m_region; };
#endif // MOBILE_NUMBERS_RANGE_H |
Таблица 16. Файл lib/mobile_numbers_range.cpp
lib/mobile_numbers_range.cpp |
#include "mobile_numbers_range.h"
MobileNumbersRange::MobileNumbersRange() {}
MobileNumbersRange::MobileNumbersRange(std::string operator_code, std::string caller_code_from, std::string caller_code_to, std::wstring mobile_operator, std::wstring region) { set_operator_code(operator_code); set_caller_code(caller_code_from, caller_code_to); set_operator_name(mobile_operator); set_region(region); }
MobileNumbersRange::MobileNumbersRange(int operator_code, int caller_code_from, int caller_code_to, std::wstring mobile_operator, std::wstring region) { set_operator_code_i(operator_code); set_caller_code_i(caller_code_from, caller_code_to); set_operator_name(mobile_operator); set_region(region); }
// getters
int MobileNumbersRange::operator_code_i() const { return m_operator_code; }
int MobileNumbersRange::caller_code_from_i() const { return m_caller_code_from; }
int MobileNumbersRange::caller_code_to_i() const { return m_caller_code_to; }
std::string MobileNumbersRange::operator_code() const { std::string s = std::to_string(m_operator_code); if (s.size() < MobileNumberDefines::OPERATOR_CODE_DIGITS) { return std::string(MobileNumberDefines::OPERATOR_CODE_DIGITS - s.size(), L'0') + s; } return s; }
std::string MobileNumbersRange::caller_code_from() const { std::string s = std::to_string(m_caller_code_from); if (s.size() < MobileNumberDefines::CALLER_CODE_DIGITS) { return std::string(MobileNumberDefines::CALLER_CODE_DIGITS - s.size(), L'0') + s; } return s; }
std::string MobileNumbersRange::caller_code_to() const { std::string s = std::to_string(m_caller_code_to); if (s.size() < MobileNumberDefines::CALLER_CODE_DIGITS) { return std::string(MobileNumberDefines::CALLER_CODE_DIGITS - s.size(), L'0') + s; } return s; }
std::wstring MobileNumbersRange::operator_name() const { return m_operator_name; }
std::wstring MobileNumbersRange::region() const { return m_region; }
// setters
void MobileNumbersRange::set_operator_code_i(int operator_code) { if (operator_code < 0 || operator_code > MobileNumberDefines::MAX_OPERATOR_CODE) { throw std::invalid_argument("set_operator_code_i -> operator_code = " + std::to_string(operator_code)); } m_operator_code = operator_code; }
void MobileNumbersRange::set_caller_code_i(int caller_code_from, int caller_code_to) { if (caller_code_from < 0 || caller_code_from > MobileNumberDefines::MAX_CALLER_CODE) { throw std::invalid_argument("set_caller_code_i -> caller_code_from = " + std::to_string(caller_code_from)); } if (caller_code_to < 0 || caller_code_to > MobileNumberDefines::MAX_CALLER_CODE) { throw std::invalid_argument("set_caller_code_i -> caller_code_to = " + std::to_string(caller_code_to)); } m_caller_code_from = caller_code_from; m_caller_code_to = caller_code_to; }
void MobileNumbersRange::set_operator_code(const std::wstring &operator_code) { set_operator_code(std::string(operator_code.begin(), operator_code.end())); }
void MobileNumbersRange::set_operator_code(const std::string &operator_code) { if (operator_code.size() != MobileNumberDefines::OPERATOR_CODE_DIGITS) { throw std::invalid_argument("set_operator_code -> operator_code.size() != " + std::to_string(MobileNumberDefines::OPERATOR_CODE_DIGITS)); } auto is_operator_code = std::all_of(operator_code.begin(), operator_code.end(), static_cast<int (*)(int)>(std::isdigit)); if (!is_operator_code) { throw std::invalid_argument("set_operator_code -> operator_code -- invalid argument: " + std::string(operator_code.begin(), operator_code.end())); } m_operator_code = std::stoi(operator_code); }
void MobileNumbersRange::set_caller_code(const std::wstring &caller_code_from, const std::wstring &caller_code_to) { set_caller_code(std::string(caller_code_from.begin(), caller_code_from.end()), std::string(caller_code_to.begin(), caller_code_to.end())); }
void MobileNumbersRange::set_caller_code(const std::string &caller_code_from, const std::string &caller_code_to) { if (caller_code_from.size() != MobileNumberDefines::CALLER_CODE_DIGITS) { throw std::invalid_argument("set_caller_code -> caller_code_from.size() != " + std::to_string(MobileNumberDefines::CALLER_CODE_DIGITS)); } if (caller_code_to.size() != MobileNumberDefines::CALLER_CODE_DIGITS) { throw std::invalid_argument("set_caller_code -> caller_code_to.size() != " + std::to_string(MobileNumberDefines::CALLER_CODE_DIGITS)); } auto is_caller_code_from = std::all_of(caller_code_from.begin(), caller_code_from.end(), static_cast<int (*)(int)>(std::isdigit)); if (!is_caller_code_from) { throw std::invalid_argument("set_caller_code -> caller_code_from -- invalid argument: " + std::string(caller_code_from.begin(), caller_code_from.end())); }
auto is_caller_code_to = std::all_of(caller_code_to.begin(), caller_code_to.end(), static_cast<int (*)(int)>(std::isdigit)); if (!is_caller_code_to) { throw std::invalid_argument("set_caller_code -> caller_code_to -- invalid argument: " + std::string(caller_code_to.begin(), caller_code_to.end())); } m_caller_code_from = std::stoi(caller_code_from); m_caller_code_to = std::stoi(caller_code_to); }
void MobileNumbersRange::set_operator_name(const std::wstring &mobile_operator) { m_operator_name = mobile_operator; }
void MobileNumbersRange::set_region(const std::wstring ®ion) { m_region = region; }
// check int MobileNumbersRange::cmp_in_range(long caller_code) const { long t = static_cast<long>(m_operator_code) * (MobileNumberDefines::MAX_CALLER_CODE + 1); long from = t + m_caller_code_from; if (caller_code < from) { return -1; } long to = t + m_caller_code_to; if (caller_code > to) { return 1; } return 0; }
bool operator<(const MobileNumbersRange &range1, const MobileNumbersRange &range2) { return range1.m_operator_code < range2.m_operator_code || (range1.m_operator_code == range2.m_operator_code && range1.m_caller_code_from < range2.m_caller_code_from); } |
Таблица 17. Файл lib/mobile_numbers_data.h
lib/mobile_numbers_data.h |
#ifndef MOBILE_NUMBERS_DATA_H #define MOBILE_NUMBERS_DATA_H
#include <istream> #include <string> #include <regex> #include <deque> #include <algorithm> #include <stdexcept> #include "mobile_numbers_range.h" #include "mobile_number_defines.h"
class MobileNumbersData { public: MobileNumbersData(); void add_data(std::wistream &); MobileNumbersRange find_with_check(std::string) const; MobileNumbersRange find_without_check(std::string) const; std::string generate() const; static bool is_mobile_number(const std::string &);
private: std::deque<MobileNumbersRange> m_data; };
#endif // MOBILE_NUMBERS_DATA_H |
Таблица 18. Файл lib/mobile_numbers_data.cpp
lib/mobile_numbers_data.cpp |
#include "mobile_numbers_data.h"
MobileNumbersData::MobileNumbersData() { }
void MobileNumbersData::add_data(std::wistream &stream) { bool maybe_header = true; MobileNumbersRange range; size_t m_data_size_from = m_data.size(); std::match_results<std::wstring::const_iterator> sm; for (std::wstring line; std::getline(stream, line);) { if (std::regex_match(line, sm, MobileNumberDefines::mobile_numbers_range_regex)) { range.set_operator_code(sm.str(1)); range.set_caller_code(sm.str(2), sm.str(3)); range.set_operator_name(sm.str(4)); range.set_region(sm.str(5)); m_data.emplace_back(range); } else { if (maybe_header && !(L'0' <= line[0] && line[0] <= L'9')) { maybe_header = false; } else { throw std::invalid_argument( std::string("Argument is invalid: ") + std::string(line.begin(), line.end())); } } } auto it = m_data.begin(); std::advance(it, m_data_size_from); if (!std::is_sorted(it, m_data.end())) { std::sort(it, m_data.end()); } std::inplace_merge(m_data.begin(), it, m_data.end()); }
MobileNumbersRange MobileNumbersData::find_with_check(std::string mobile_number) const { std::smatch sm; if (!std::regex_match(mobile_number, sm, MobileNumberDefines::mobile_number_regex)) { throw std::out_of_range("Invalid mobile number."); } return find_without_check(sm.str(2) + sm.str(3) + sm.str(4) + sm.str(5)); }
MobileNumbersRange MobileNumbersData::find_without_check(std::string mobile_number) const { if (m_data.empty()) { throw std::out_of_range("Data is empty. Use method 'add' before 'find'."); } size_t left = 0, right = m_data.size() - 1, mid; long target = std::stol(mobile_number); while (left <= right && right != std::wstring::npos) { mid = left + ((right - left) >> 1); int cmp = m_data[mid].cmp_in_range(target); if (cmp == -1) { right = mid - 1; } else if (cmp == 1) { left = mid + 1; } else { return m_data[mid]; } } throw std::range_error("Not found."); }
std::string MobileNumbersData::generate() const { MobileNumbersRange range = m_data[RandomGen::get_random_size_t_number(0, m_data.size() - 1)]; std::string operator_code = range.operator_code(); std::string caller_code = std::to_string(RandomGen::get_random_int_number(range.caller_code_from_i(), range.caller_code_to_i())); return std::string(MobileNumberDefines::OPERATOR_CODE_DIGITS - operator_code.size(), '0') + operator_code + std::string(MobileNumberDefines::CALLER_CODE_DIGITS - caller_code.size(), '0') + caller_code; }
bool MobileNumbersData::is_mobile_number(const std::string &arg) { return std::regex_match(arg, MobileNumberDefines::mobile_number_regex); } |
Таблица 19. Сборочный файл lib/Makefile
lib/Makefile |
.PHONY : all clean CXX=g++ CXXFLAGS=-std=c++17 -O4 -Werror -pedantic-errors -Wredundant-decls \ -Wall -Wextra -Wpedantic -Wcast-align -Wcast-qual \ -Wconversion -Wctor-dtor-privacy -Wduplicated-branches \ -Wduplicated-cond -Wextra-semi -Wfloat-equal -Wlogical-op \ -Wnon-virtual-dtor -Woverloaded-virtual -Wsign-conversion -Wsign-promo
all: $(patsubst %.cpp, %.o, $(wildcard *.cpp))
%.o: %.cpp %.h Makefile $(CXX) $(CXXFLAGS) -c -o $@ $<
clean: rm -f *.o |
Таблица 20. Сборочный файл Makefile
Makefile |
.PHONY : all clean CXX=g++ CXXFLAGS=-std=c++17 -O4 -Werror -pedantic-errors -Wredundant-decls \ -Wall -Wextra -Wpedantic -Wcast-align -Wcast-qual \ -Wconversion -Wctor-dtor-privacy -Wduplicated-branches \ -Wduplicated-cond -Wextra-semi -Wfloat-equal -Wlogical-op \ -Wnon-virtual-dtor -Woverloaded-virtual -Wsign-conversion -Wsign-promo LIB_HEADERS=$(wildcard lib/*.h) LIB_SOURCES=$(wildcard lib/*.cpp) LIB_OBJECTS=$(patsubst %.cpp, %.o, $(LIB_SOURCES))
all: main test
main: $(LIB_OBJECTS) main.o $(CXX) $(CXXFLAGS) -o main $(LIB_OBJECTS) main.o
test: $(LIB_OBJECTS) test.o $(CXX) $(CXXFLAGS) -o test $(LIB_OBJECTS) test.o
$(LIB_OBJECTS): $(LIB_SOURCES) $(LIB_HEADERS) @cd lib && $(MAKE) && cd ../
%.o: %.cpp Makefile $(CXX) $(CXXFLAGS) -c -o $@ $<
clean: rm -f *.o |
Решение с деревом цифр и минимальным масками диапазонов состоит из 11 файлов, представленных в таблицах 21-31.
Таблица 21. Файл main.cpp
main.cpp |
#include <string> #include <iostream> #include <fstream> #include <filesystem> #include <stdexcept> #include "lib/mobile_numbers_data.h"
namespace fs = std::filesystem;
std::vector<fs::path> get_files_in_directory(const std::wstring &directory, std::wstring ext) { std::vector<fs::path> files_vector; for (const auto &file : fs::directory_iterator(directory)) { fs::path filepath = file.path(); if (fs::is_regular_file(filepath) && (ext.empty() || filepath.extension() == ext)) { files_vector.emplace_back(filepath); } } return files_vector; }
std::wstring replace(const std::wstring &str, const std::wstring &substr, const std::wstring &repl_substr) { size_t pos = str.find(substr); if (pos != std::wstring::npos) { return str.substr(0, pos) + repl_substr + str.substr(pos + substr.size(), std::string::npos); } return str; }
int main(int argc, char *argv[]) { // set locale (required for wstring) std::locale::global(std::locale("C.UTF-8")); // check args if (argc != 2) { std::wcout << "Use: ./main {mobile_number}" << std::endl; return 1; } // check mobile number std::string mobile_number(argv[1]); if (!MobileNumbersData::is_mobile_number(mobile_number)) { std::wcout << "Invalid mobile number format." << std::endl; return 2; } // get csv files in current directory std::vector<fs::path> files_vector = get_files_in_directory(L"./", L".csv"); if (files_vector.empty()) { std::wcout << "CSV files not found." << std::endl; return 3; } try { MobileNumbersData mobile_numbers_data; for (const auto &file : files_vector) { std::wifstream wstream(file); if (!wstream.is_open()) { std::wcout << "File \"" << file.wstring() << "\" skipped (open error)." << std::endl; continue; } mobile_numbers_data.add_data(wstream); } auto range = mobile_numbers_data.find_with_check(mobile_number); auto mobile_operator = range.operator_name(); auto region = range.region(); region = replace(region, L";", L", "); region = replace(region, L"|", L", "); std::wcout << mobile_operator << ", " << region << std::endl; } catch (const std::exception &e) { std::wcout << e.what() << std::endl; return 4; } return 0; } |
Таблица 22. Файл test.cpp
test.cpp |
#include <string> #include <iostream> #include <fstream> #include <filesystem> #include <stdexcept> #include <chrono> #include "lib/mobile_numbers_data.h"
namespace fs = std::filesystem;
// Get the current time in seconds // This function should be used only for measuring time // (first call, algorithm, second call; then the difference between the values) #if defined(_WIN32) #include <Windows.h> double get_proc_time() { FILETIME createTime, exitTime, kernelTime, userTime; if (GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime) != -1) return (ULARGE_INTEGER{{userTime.dwLowDateTime, userTime.dwHighDateTime}}).QuadPart / 10000000.L; return 0; } #else double get_proc_time() { return static_cast<double>(clock()) / CLOCKS_PER_SEC; } #endif
std::vector<fs::path> get_files_in_directory(const std::wstring &directory, std::wstring ext) { std::vector<fs::path> files_vector; for (const auto &file : fs::directory_iterator(directory)) { fs::path filepath = file.path(); if (fs::is_regular_file(filepath) && (ext.empty() || filepath.extension() == ext)) { files_vector.emplace_back(filepath); } } return files_vector; }
std::wstring replace(const std::wstring &str, const std::wstring &substr_from, const std::wstring &substr_to) { size_t pos = str.find(substr_from); if (pos != std::wstring::npos) { return str.substr(0, pos) + substr_to + str.substr(pos + substr_from.size(), std::string::npos); } return str; }
int main(int argc, char *argv[]) { // set locale (required for wstring) std::locale::global(std::locale("C.UTF-8")); // check args if (argc != 2) { std::wcout << "Use: ./test {number of tests}" << std::endl; return 1; } int n_tests = 0; try { n_tests = std::atoi(argv[1]); if (n_tests <= 0) { throw std::invalid_argument("Arg {number of tests} is non-positive number."); } } catch (const std::exception &e) { std::wcout << e.what() << std::endl; return 2; } // find csv files in current directory std::vector<fs::path> files_vector = get_files_in_directory(L"./", L".csv"); if (files_vector.empty()) { std::wcout << "CSV files not found." << std::endl; return 3; } try { // start testing MobileNumbersData mobile_numbers_data; double s = 0, start, finish; for (const auto &file : files_vector) { std::wifstream wstream(file); if (!wstream.is_open()) { std::wcout << "File \"" << file.wstring() << "\" skipped (open error)." << std::endl; continue; } start = get_proc_time(); mobile_numbers_data.add_data(wstream); finish = get_proc_time(); s += finish - start; } std::wcout << "-- Adding data from files: " << s << " seconds" << std::endl; s = 0; for (int i = 0; i < n_tests; ++i) { auto mobile_number = mobile_numbers_data.generate(); start = get_proc_time(); auto range = mobile_numbers_data.find_without_check(mobile_number); finish = get_proc_time(); s += finish - start; } std::wcout << "== Without check results" << std::endl; std::wcout << "-- Testing time: " << s << " seconds" << std::endl; s = 0; for (int i = 0; i < n_tests; ++i) { auto mobile_number = mobile_numbers_data.generate(); start = get_proc_time(); auto range = mobile_numbers_data.find_with_check(mobile_number); finish = get_proc_time(); s += finish - start; } std::wcout << "== With check results" << std::endl; std::wcout << "-- Testing time: " << s << " seconds" << std::endl; } catch (const std::exception &e) { std::wcout << e.what() << std::endl; return 4; } return 0; } |
Таблица 23. Файл lib/digits_tree.h
lib/digits_tree.h |
#ifndef DIGITS_TREE_H #define DIGITS_TREE_H
#include <cstdint> #include <memory> #include <cstdlib> #include <stdexcept> #include <deque> #include "mobile_number_defines.h" #include "mobile_numbers_range.h"
template <size_t D = 10> class DigitsTree { public: DigitsTree() : m_size(0) { }
void push(const std::string &str, const MobileNumbersRange &obj) { ++m_size; if (str.empty()) { if (!m_current) { m_current = std::make_unique<MobileNumbersRange>(obj); return; } else { throw std::invalid_argument("push -> obj already exists"); } m_current = std::make_unique<MobileNumbersRange>(obj); } char first_digit = str[0]; if ('0' <= first_digit && first_digit <= '9') { size_t index = static_cast<size_t>(first_digit - '0'); if (!m_ranges[index]) { m_ranges[index] = std::make_unique<DigitsTree>(); } m_ranges[index]->push(str.substr(1), obj); } else { if (!m_current) { m_current = std::make_unique<MobileNumbersRange>(obj); } else { throw std::invalid_argument("push -> obj already exists"); } } }
MobileNumbersRange find(const std::string &str, size_t shift_ = 0) const { if (empty()) { std::out_of_range("Data is empty. Use method 'push' before 'find'."); } if (m_current) { return *m_current; } if (str.empty()) { throw std::invalid_argument("find -> str is empty"); } if (shift_ >= str.size()) { throw std::invalid_argument("find -> shift >= str.size()"); } if (!('0' <= str[shift_] && str[shift_] <= '9')) { throw std::invalid_argument("find -> str[shift_] not in [0, 9]"); } size_t index = static_cast<size_t>(str[shift_] - '0'); if (m_ranges[index]) { return m_ranges[index]->find(str, shift_ + 1); } throw std::out_of_range("Not found."); }
inline bool empty() const { return m_size == 0; }
MobileNumbersRange get_random() const { if (empty()) { std::out_of_range("Data is empty. Use method 'add' before 'find'."); } if (!m_current) { std::deque<size_t> d; for (size_t i = 0; i < D; ++i) { if (m_ranges[i]) { d.push_back(i); } } if (!d.empty()) { return m_ranges[d[RandomGen::get_random_size_t_number(0, d.size() - 1)]]->get_random(); } else { std::invalid_argument("get_random -> something is wrong"); } } return *m_current; }
size_t size() const { return m_size; }
private: std::unique_ptr<DigitsTree> m_ranges[D]; std::unique_ptr<MobileNumbersRange> m_current; size_t m_size; };
#endif // DIGITS_TREE_H |
Таблица 24. Файл lib/mobile_number_defines.h
lib/mobile_number_defines.h |
#ifndef MOBILE_NUMBER_DEFINES_H #define MOBILE_NUMBER_DEFINES_H
#include <cmath> #include <string> #include <regex> #include <random> #include <functional>
namespace MobileNumberDefines { constexpr size_t COUNTRY_CODE_DIGITS = 1; constexpr size_t OPERATOR_CODE_DIGITS = 3; constexpr size_t CALLER_CODE_DIGITS = 7; constexpr int MAX_COUNTRY_CODE = std::pow(10, COUNTRY_CODE_DIGITS) - 1; constexpr int MAX_OPERATOR_CODE = std::pow(10, OPERATOR_CODE_DIGITS) - 1; constexpr int MAX_CALLER_CODE = std::pow(10, CALLER_CODE_DIGITS) - 1;
const std::regex mobile_number_regex("^(?:(\\+?\\d)[\\- ]?)?(?:\\(?(\\d{3})\\)?[\\- ]?)(\\d{3})[\\- ]?(\\d{2})[\\- ]?(\\d{2})$"); /* Can work with next mobile numbers +79811742698 +7(981)1742698 +7-(981)-174-26-98 89811742698 9811742698 (981) 174 2698 (981)-174-2698 981-174-2698 981 174 2698 9811742698 ... country_code -- group #1 operator_code -- group #2 caller_code -- groups #3, #4 and #5 */
const std::basic_regex<wchar_t> mobile_numbers_range_regex(L"^(\\d{3});(\\d{7});(\\d{7});[^;]*?;([^;]*);(.*)$"); /* Can work with next mobile numbers range АВС/ DEF;От;До;Емкость;Оператор;Регион 301;2110000;2129999;20000;ПАО "Ростелеком";г. Улан-Удэ|Республика Бурятия 301;2150000;2169999;20000;ПАО "Ростелеком";г. Улан-Удэ|Республика Бурятия 301;2180000;2189999;10000;ПАО "Ростелеком";г. Улан-Удэ|Республика Бурятия ... operator_code -- group #1 caller_code_from -- group #2 caller_code_to -- group #3 mobile_operator -- group #4 region -- group #5 */ }
namespace RandomGen { extern std::random_device random_device_mn; extern std::mt19937 mt; extern std::uniform_int_distribution<size_t> size_t_distribution; extern std::uniform_int_distribution<int> int_distribution; extern size_t get_random_size_t_number(size_t, size_t); extern int get_random_int_number(int, int); }
#endif // MOBILE_NUMBER_DEFINES_H |
Таблица 25. Файл lib/mobile_number_defines.cpp
lib/mobile_number_defines.cpp |
#include "mobile_number_defines.h"
std::random_device RandomGen::random_device_mn;
std::mt19937 RandomGen::mt = std::mt19937(RandomGen::random_device_mn());
std::uniform_int_distribution<size_t> RandomGen::size_t_distribution(0, std::numeric_limits<size_t>::max());
std::uniform_int_distribution<int> RandomGen::int_distribution(0, std::numeric_limits<int>::max());
size_t RandomGen::get_random_size_t_number(size_t from, size_t to) { return from + RandomGen::size_t_distribution(RandomGen::mt) % (to - from + 1); }
int RandomGen::get_random_int_number(int from, int to) { return from + RandomGen::int_distribution(RandomGen::mt) % (to - from + 1); } |
Таблица 26. Файл lib/mobile_numbers_range.h
lib/mobile_numbers_range.h |
#ifndef MOBILE_NUMBERS_RANGE_H #define MOBILE_NUMBERS_RANGE_H
#include <list> #include <cmath> #include <string> #include <algorithm> #include <stdexcept> #include "mobile_number_defines.h"
class MobileNumbersRange { public: MobileNumbersRange(); MobileNumbersRange(std::string, std::string, std::string, std::wstring, std::wstring); MobileNumbersRange(int, int, int, std::wstring, std::wstring); // getters int operator_code_i() const; int caller_code_from_i() const; int caller_code_to_i() const; std::string operator_code() const; std::string caller_code_from() const; std::string caller_code_to() const; std::wstring operator_name() const; std::wstring region() const; // setters void set_operator_code_i(int); void set_caller_code_i(int, int); void set_operator_code(const std::string &); void set_operator_code(const std::wstring &); void set_caller_code(const std::string &, const std::string &); void set_caller_code(const std::wstring &, const std::wstring &); void set_operator_name(const std::wstring &); void set_region(const std::wstring &); std::list<std::string> split();
private: int m_operator_code, m_caller_code_from, m_caller_code_to; std::wstring m_operator_name, m_region; };
#endif // MOBILE_NUMBERS_RANGE_H |
Таблица 27. Файл lib/mobile_numbers_range.cpp
lib/mobile_numbers_range.cpp |
#include "mobile_numbers_range.h"
MobileNumbersRange::MobileNumbersRange() {}
MobileNumbersRange::MobileNumbersRange(std::string operator_code, std::string caller_code_from, std::string caller_code_to, std::wstring mobile_operator, std::wstring region) { set_operator_code(operator_code); set_caller_code(caller_code_from, caller_code_to); set_operator_name(mobile_operator); set_region(region); }
MobileNumbersRange::MobileNumbersRange(int operator_code, int caller_code_from, int caller_code_to, std::wstring mobile_operator, std::wstring region) { set_operator_code_i(operator_code); set_caller_code_i(caller_code_from, caller_code_to); set_operator_name(mobile_operator); set_region(region); }
// getters
int MobileNumbersRange::operator_code_i() const { return m_operator_code; }
int MobileNumbersRange::caller_code_from_i() const { return m_caller_code_from; }
int MobileNumbersRange::caller_code_to_i() const { return m_caller_code_to; }
std::string MobileNumbersRange::operator_code() const { std::string s = std::to_string(m_operator_code); if (s.size() < MobileNumberDefines::OPERATOR_CODE_DIGITS) { return std::string(MobileNumberDefines::OPERATOR_CODE_DIGITS - s.size(), L'0') + s; } return s; }
std::string MobileNumbersRange::caller_code_from() const { std::string s = std::to_string(m_caller_code_from); if (s.size() < MobileNumberDefines::CALLER_CODE_DIGITS) { return std::string(MobileNumberDefines::CALLER_CODE_DIGITS - s.size(), L'0') + s; } return s; }
std::string MobileNumbersRange::caller_code_to() const { std::string s = std::to_string(m_caller_code_to); if (s.size() < MobileNumberDefines::CALLER_CODE_DIGITS) { return std::string(MobileNumberDefines::CALLER_CODE_DIGITS - s.size(), L'0') + s; } return s; }
std::wstring MobileNumbersRange::operator_name() const { return m_operator_name; }
std::wstring MobileNumbersRange::region() const { return m_region; }
// setters
void MobileNumbersRange::set_operator_code_i(int operator_code) { if (operator_code < 0 || operator_code > MobileNumberDefines::MAX_OPERATOR_CODE) { throw std::invalid_argument("set_operator_code_i -> operator_code = " + std::to_string(operator_code)); } m_operator_code = operator_code; }
void MobileNumbersRange::set_caller_code_i(int caller_code_from, int caller_code_to) { if (caller_code_from < 0 || caller_code_from > MobileNumberDefines::MAX_CALLER_CODE) { throw std::invalid_argument("set_caller_code_i -> caller_code_from = " + std::to_string(caller_code_from)); } if (caller_code_to < 0 || caller_code_to > MobileNumberDefines::MAX_CALLER_CODE) { throw std::invalid_argument("set_caller_code_i -> caller_code_to = " + std::to_string(caller_code_to)); } m_caller_code_from = caller_code_from; m_caller_code_to = caller_code_to; }
void MobileNumbersRange::set_operator_code(const std::wstring &operator_code) { set_operator_code(std::string(operator_code.begin(), operator_code.end())); }
void MobileNumbersRange::set_operator_code(const std::string &operator_code) { if (operator_code.size() != MobileNumberDefines::OPERATOR_CODE_DIGITS) { throw std::invalid_argument("set_operator_code -> operator_code.size() != " + std::to_string(MobileNumberDefines::OPERATOR_CODE_DIGITS)); } auto is_operator_code = std::all_of(operator_code.begin(), operator_code.end(), static_cast<int (*)(int)>(std::isdigit)); if (!is_operator_code) { throw std::invalid_argument("set_operator_code -> operator_code -- invalid argument: " + std::string(operator_code.begin(), operator_code.end())); } m_operator_code = std::stoi(operator_code); }
void MobileNumbersRange::set_caller_code(const std::wstring &caller_code_from, const std::wstring &caller_code_to) { set_caller_code(std::string(caller_code_from.begin(), caller_code_from.end()), std::string(caller_code_to.begin(), caller_code_to.end())); }
void MobileNumbersRange::set_caller_code(const std::string &caller_code_from, const std::string &caller_code_to) { if (caller_code_from.size() != MobileNumberDefines::CALLER_CODE_DIGITS) { throw std::invalid_argument("set_caller_code -> caller_code_from.size() != " + std::to_string(MobileNumberDefines::CALLER_CODE_DIGITS)); } if (caller_code_to.size() != MobileNumberDefines::CALLER_CODE_DIGITS) { throw std::invalid_argument("set_caller_code -> caller_code_to.size() != " + std::to_string(MobileNumberDefines::CALLER_CODE_DIGITS)); } auto is_caller_code_from = std::all_of(caller_code_from.begin(), caller_code_from.end(), static_cast<int (*)(int)>(std::isdigit)); if (!is_caller_code_from) { throw std::invalid_argument("set_caller_code -> caller_code_from -- invalid argument: " + std::string(caller_code_from.begin(), caller_code_from.end())); }
auto is_caller_code_to = std::all_of(caller_code_to.begin(), caller_code_to.end(), static_cast<int (*)(int)>(std::isdigit)); if (!is_caller_code_to) { throw std::invalid_argument("set_caller_code -> caller_code_to -- invalid argument: " + std::string(caller_code_to.begin(), caller_code_to.end())); } m_caller_code_from = std::stoi(caller_code_from); m_caller_code_to = std::stoi(caller_code_to); }
void MobileNumbersRange::set_operator_name(const std::wstring &mobile_operator) { m_operator_name = mobile_operator; }
void MobileNumbersRange::set_region(const std::wstring ®ion) { m_region = region; }
std::list<std::string> MobileNumbersRange::split() { auto get_number_of_digits = [](int x) -> int { return static_cast<int>(std::log10(x) + 1); }; std::list<std::string> range_splitted; int cct = m_caller_code_to; while (cct >= m_caller_code_from) { int digits_init = get_number_of_digits(cct); if (cct % 10 == 9 && cct - m_caller_code_from >= 9) { int digits = get_number_of_digits(cct - m_caller_code_from + 10) - 1; int pw = static_cast<int>(std::pow(10, digits)); for (int i = 0; i <= digits; ++i, pw /= 10) { if (cct % pw == pw - 1) { int t = cct / pw; std::string t1; if (t != 0) { t1 = std::to_string(t); } std::string t2 = std::string(static_cast<size_t>(digits_init) - t1.size(), '?'); range_splitted.push_back(t1 + t2); break; } } cct -= pw; } else { if (range_splitted.empty() || range_splitted.back() != "?" || cct != 0) { range_splitted.push_back(std::to_string(cct)); --cct; } else { break; } } } for (auto &x : range_splitted) { if (x.size() < MobileNumberDefines::CALLER_CODE_DIGITS) { x = std::string(MobileNumberDefines::CALLER_CODE_DIGITS - x.size(), '0') + x; } } return range_splitted; } |
Таблица 28. Файл lib/mobile_numbers_data.h
lib/mobile_numbers_data.h |
#ifndef MOBILE_NUMBERS_DATA_H #define MOBILE_NUMBERS_DATA_H
#include <istream> #include <string> #include <regex> #include <algorithm> #include <stdexcept> #include "mobile_numbers_range.h" #include "digits_tree.h" #include "mobile_number_defines.h"
class MobileNumbersData { public: MobileNumbersData(); void add_data(std::wistream &); MobileNumbersRange find_with_check(std::string) const; MobileNumbersRange find_without_check(std::string) const; std::string generate() const; static bool is_mobile_number(const std::string &);
private: DigitsTree<> m_data; };
#endif // MOBILE_NUMBERS_DATA_H |
Таблица 29. Файл lib/mobile_numbers_data.cpp
lib/mobile_numbers_data.cpp |
#include "mobile_numbers_data.h"
MobileNumbersData::MobileNumbersData() { }
void MobileNumbersData::add_data(std::wistream &stream) { bool maybe_header = true; MobileNumbersRange range; std::match_results<std::wstring::const_iterator> sm; for (std::wstring line; std::getline(stream, line);) { if (std::regex_match(line, sm, MobileNumberDefines::mobile_numbers_range_regex)) { range.set_operator_code(sm.str(1)); range.set_caller_code(sm.str(2), sm.str(3)); range.set_operator_name(sm.str(4)); range.set_region(sm.str(5)); std::string operator_code = range.operator_code(); auto range_splitted = range.split(); for (const auto &x : range_splitted) { m_data.push(operator_code + x, range); } } else { if (maybe_header && !(L'0' <= line[0] && line[0] <= L'9')) { maybe_header = false; } else { throw std::invalid_argument( std::string("Argument is invalid: ") + std::string(line.begin(), line.end())); } } } }
MobileNumbersRange MobileNumbersData::find_with_check(std::string mobile_number) const { std::smatch sm; if (!std::regex_match(mobile_number, sm, MobileNumberDefines::mobile_number_regex)) { throw std::out_of_range("Invalid mobile number."); } return m_data.find(sm.str(2) + sm.str(3) + sm.str(4) + sm.str(5)); }
MobileNumbersRange MobileNumbersData::find_without_check(std::string mobile_number) const { return m_data.find(mobile_number); }
std::string MobileNumbersData::generate() const { MobileNumbersRange range = m_data.get_random(); std::string operator_code = range.operator_code(); std::string caller_code = std::to_string(RandomGen::get_random_int_number(range.caller_code_from_i(), range.caller_code_to_i())); return std::string(MobileNumberDefines::OPERATOR_CODE_DIGITS - operator_code.size(), '0') + operator_code + std::string(MobileNumberDefines::CALLER_CODE_DIGITS - caller_code.size(), '0') + caller_code; }
bool MobileNumbersData::is_mobile_number(const std::string &arg) { return std::regex_match(arg, MobileNumberDefines::mobile_number_regex); } |
Таблица 30. Сборочный файл lib/Makefile
lib/Makefile |
.PHONY : all clean CXX=g++ CXXFLAGS=-std=c++17 -O4 -Werror -pedantic-errors -Wredundant-decls \ -Wall -Wextra -Wpedantic -Wcast-align -Wcast-qual \ -Wconversion -Wctor-dtor-privacy -Wduplicated-branches \ -Wduplicated-cond -Wextra-semi -Wfloat-equal -Wlogical-op \ -Wnon-virtual-dtor -Woverloaded-virtual -Wsign-conversion -Wsign-promo
all: $(patsubst %.cpp, %.o, $(wildcard *.cpp))
%.o: %.cpp %.h Makefile $(CXX) $(CXXFLAGS) -c -o $@ $<
clean: rm -f *.o |
Таблица 31. Сборочный файл Makefile
Makefile |
.PHONY : all clean CXX=g++ CXXFLAGS=-std=c++17 -O4 -Werror -pedantic-errors -Wredundant-decls \ -Wall -Wextra -Wpedantic -Wcast-align -Wcast-qual \ -Wconversion -Wctor-dtor-privacy -Wduplicated-branches \ -Wduplicated-cond -Wextra-semi -Wfloat-equal -Wlogical-op \ -Wnon-virtual-dtor -Woverloaded-virtual -Wsign-conversion -Wsign-promo LIB_HEADERS=$(wildcard lib/*.h) LIB_SOURCES=$(wildcard lib/*.cpp) LIB_OBJECTS=$(patsubst %.cpp, %.o, $(LIB_SOURCES))
all: main test
main: $(LIB_OBJECTS) main.o $(CXX) $(CXXFLAGS) -o main $(LIB_OBJECTS) main.o
test: $(LIB_OBJECTS) test.o $(CXX) $(CXXFLAGS) -o test $(LIB_OBJECTS) test.o
$(LIB_OBJECTS): $(LIB_SOURCES) $(LIB_HEADERS) @cd lib && $(MAKE) && cd ../
%.o: %.cpp Makefile $(CXX) $(CXXFLAGS) -c -o $@ $<
clean: rm -f *.o |
