• c++ 图论学习3


    使用 C++ 来实现一个针对企业内部文件的网络爬虫,尽可能利用 C++的新特性和图的遍历算法

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #include
    12. namespace fs = std::filesystem;
    13. class InternalCrawler {
    14. private:
    15. std::unordered_set visited;
    16. std::queue to_visit;
    17. std::mutex mtx;
    18. std::atomic<bool> running{true};
    19. std::vector threads;
    20. std::regex file_pattern;
    21. fs::path root_dir;
    22. void crawl_file(const fs::path& file_path) {
    23. std::ifstream file(file_path);
    24. std::string line;
    25. while (std::getline(file, line)) {
    26. std::smatch match;
    27. if (std::regex_search(line, match, file_pattern)) {
    28. std::string new_file = match.str();
    29. fs::path new_path = root_dir / new_file;
    30. if (fs::exists(new_path)) {
    31. std::lock_guard lock(mtx);
    32. if (visited.find(new_file) == visited.end()) {
    33. to_visit.push(new_file);
    34. visited.insert(new_file);
    35. }
    36. }
    37. }
    38. }
    39. }
    40. void worker() {
    41. while (running) {
    42. std::string current_file;
    43. {
    44. std::lock_guard lock(mtx);
    45. if (to_visit.empty()) {
    46. std::this_thread::sleep_for(std::chrono::milliseconds(100));
    47. continue;
    48. }
    49. current_file = to_visit.front();
    50. to_visit.pop();
    51. }
    52. crawl_file(root_dir / current_file);
    53. }
    54. }
    55. public:
    56. InternalCrawler(const fs::path& root, const std::string& pattern, int num_threads = 4)
    57. : root_dir(root), file_pattern(pattern) {
    58. for (int i = 0; i < num_threads; ++i) {
    59. threads.emplace_back(&InternalCrawler::worker, this);
    60. }
    61. }
    62. void start(const std::string& start_file) {
    63. to_visit.push(start_file);
    64. visited.insert(start_file);
    65. }
    66. void stop() {
    67. running = false;
    68. for (auto& thread : threads) {
    69. thread.join();
    70. }
    71. }
    72. void print_visited() {
    73. for (const auto& file : visited) {
    74. std::cout << file << std::endl;
    75. }
    76. }
    77. };
    78. int main() {
    79. fs::path root_dir = "/path/to/your/document/root";
    80. std::string file_pattern = R"(\b[a-zA-Z0-9_-]+\.(txt|doc|pdf)\b)";
    81. InternalCrawler crawler(root_dir, file_pattern);
    82. crawler.start("start_document.txt");
    83. // 让爬虫运行一段时间
    84. std::this_thread::sleep_for(std::chrono::seconds(30));
    85. crawler.stop();
    86. crawler.print_visited();
    87. return 0;
    88. }

    这个实现使用了以下 C++新特性和图的遍历算法:

    1. 使用 std::filesystem (C++17) 进行文件系统操作。
    2. 使用 std::threadstd::mutex (C++11) 实现多线程爬虫。
    3. 使用 std::atomic (C++11) 实现线程安全的控制标志。
    4. 使用 std::regex (C++11) 进行文件名匹配。
    5. 使用 std::unordered_set 存储已访问的文件,实现 O(1) 的查找复杂度。
    6. 使用广度优先搜索 (BFS) 算法遍历文件网络图。

    这个爬虫的工作原理如下:

    1. 从指定的起始文件开始。
    2. 使用多个线程并行处理文件。
    3. 每个线程从队列中取出一个文件,读取其内容,并使用正则表达式查找其他文件的引用。
    4. 如果找到新的文件引用,检查该文件是否存在,如果存在且未被访问过,则加入队列。
    5. 使用互斥锁保护共享数据结构(visited集合和to_visit队列)。
    6. 继续这个过程,直到所有文件都被处理或手动停止爬虫。

    为了方便理解下面为每一行代码都配上了注释

    1. #include // 用于输入输出
    2. #include // 用于存储线程
    3. #include // 用于存储待访问的文件
    4. #include // 用于存储已访问的文件
    5. #include // 用于文件系统操作
    6. #include // 用于文件读取
    7. #include // 用于多线程支持
    8. #include // 用于线程同步
    9. #include // 用于原子操作
    10. #include // 用于时间相关操作
    11. #include // 用于正则表达式
    12. namespace fs = std::filesystem; // 为std::filesystem创建别名
    13. class InternalCrawler {
    14. private:
    15. std::unordered_set visited; // 存储已访问的文件
    16. std::queue to_visit; // 存储待访问的文件
    17. std::mutex mtx; // 用于保护共享数据的互斥锁
    18. std::atomic<bool> running{true}; // 控制爬虫运行状态的原子变量
    19. std::vector threads; // 存储工作线程
    20. std::regex file_pattern; // 用于匹配文件名的正则表达式
    21. fs::path root_dir; // 文档根目录
    22. void crawl_file(const fs::path& file_path) {
    23. std::ifstream file(file_path); // 打开文件
    24. std::string line;
    25. while (std::getline(file, line)) { // 逐行读取文件内容
    26. std::smatch match;
    27. if (std::regex_search(line, match, file_pattern)) { // 在行中搜索匹配的文件名
    28. std::string new_file = match.str();
    29. fs::path new_path = root_dir / new_file; // 构造新文件的完整路径
    30. if (fs::exists(new_path)) { // 检查文件是否存在
    31. std::lock_guard lock(mtx); // 锁定互斥锁
    32. if (visited.find(new_file) == visited.end()) { // 检查文件是否已被访问
    33. to_visit.push(new_file); // 将新文件加入待访问队列
    34. visited.insert(new_file); // 将新文件标记为已访问
    35. }
    36. }
    37. }
    38. }
    39. }
    40. void worker() {
    41. while (running) { // 当爬虫正在运行时
    42. std::string current_file;
    43. {
    44. std::lock_guard lock(mtx); // 锁定互斥锁
    45. if (to_visit.empty()) { // 如果没有待访问的文件
    46. std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 休眠一段时间
    47. continue;
    48. }
    49. current_file = to_visit.front(); // 获取队列前端的文件
    50. to_visit.pop(); // 从队列中移除该文件
    51. }
    52. crawl_file(root_dir / current_file); // 处理当前文件
    53. }
    54. }
    55. public:
    56. InternalCrawler(const fs::path& root, const std::string& pattern, int num_threads = 4)
    57. : root_dir(root), file_pattern(pattern) {
    58. for (int i = 0; i < num_threads; ++i) {
    59. threads.emplace_back(&InternalCrawler::worker, this); // 创建工作线程
    60. }
    61. }
    62. void start(const std::string& start_file) {
    63. to_visit.push(start_file); // 将起始文件加入待访问队列
    64. visited.insert(start_file); // 将起始文件标记为已访问
    65. }
    66. void stop() {
    67. running = false; // 停止爬虫运行
    68. for (auto& thread : threads) {
    69. thread.join(); // 等待所有工作线程结束
    70. }
    71. }
    72. void print_visited() {
    73. for (const auto& file : visited) {
    74. std::cout << file << std::endl; // 打印所有已访问的文件
    75. }
    76. }
    77. };
    78. int main() {
    79. fs::path root_dir = "/path/to/your/document/root"; // 设置文档根目录
    80. std::string file_pattern = R"(\b[a-zA-Z0-9_-]+\.(txt|doc|pdf)\b)"; // 设置文件名匹配模式
    81. InternalCrawler crawler(root_dir, file_pattern); // 创建爬虫实例
    82. crawler.start("start_document.txt"); // 开始爬取,从start_document.txt开始
    83. // 让爬虫运行30秒
    84. std::this_thread::sleep_for(std::chrono::seconds(30));
    85. crawler.stop(); // 停止爬虫
    86. crawler.print_visited(); // 打印所有已访问的文件
    87. return 0;
    88. }

  • 相关阅读:
    Servlet快速入门
    import.meta.glob() 如何导入多个目录下的资源
    Java&数组
    Spring中什么样的Bean存在线程安全问题-有状态bean
    最受欢迎的编程语言排行榜
    算法与数据结构【30天】集训营——线性表的定义及特点-顺序表的表示与实现及操作(03)
    【NDVI:注意力机制:遥感图像】
    关于api设计的一些思考-restful理想主义与商业需求的无解冲突
    UE里的Sync Groups
    【生成对抗网络 论文泛读】……StarganV1 & StarganV2……
  • 原文地址:https://blog.csdn.net/m0_61862494/article/details/140046617