在项目开发中,日志是不可或缺的一环。通过日志,我们可以随时掌握程序运行状态,排查问题也更有依据。服务端代码则是整个项目的入口,负责调用各个模块完成实际业务。
1. 日志系统实现
C/C++ 编译器提供了两个预定义宏 __FILE__ 和 __LINE__,它们能自动获取当前源文件路径和行号。利用这两个宏,我们可以在日志输出时精确定位到出错位置。
__FILE__:被替换为当前源文件的文件名或路径字符串。__LINE__:被替换为当前代码所在的行号整数。
例如在 test.cpp 第 25 行调用日志宏,日志内容就会带上 [test.cpp : 25] 的标记。
基于此,我们可以封装一个简单的日志函数。这里使用 log1 作为基础实现,支持不同级别(正常、警告、调试、致命)的输出。
#pragma once
#include <iostream>
#include <string>
#include <ctime>
#define NORMAL 1
#define WARNING 2
#define DEBUG 3
#define FATAL 4
// 宏展开后调用 log1 函数,传入级别名、消息、文件和行号
#define LOG1(LEVEL, MESSAGE) log1(#LEVEL, MESSAGE, __FILE__, __LINE__)
void log1(std::string level, std::string message, std::string file, int line) {
std::cout << "[" << level << "]"
<< "[" << time(nullptr) << "]"
<< "[" << message << "]"
<< "[" << file << " : " << line << "]"
<< std::endl;
}
这样每次调用 LOG1 时,都会自动带上时间戳、文件位置和行号,方便后续追踪。
2. Server 端入口
服务端主要职责是接收用户查询,调用搜索类处理数据,并返回 JSON 格式的结果。数据源通常存放在 data/raw_html/raw.txt,前端静态资源放在 ./wwwroot/。
主程序中实例化 Searcher 类,调用 InitSearcher 初始化索引。这里有一个细节:读取输入时使用了 fgets 而不是 cin。原因是 cin >> query 遇到空格会截断,而搜索引擎的关键词往往包含空格,fgets 可以整行读取。
读取到的缓冲区末尾可能包含换行符,需要手动去掉。处理后的查询字符串交给 Search 方法,结果存入 json_string 并输出。
#include "searcher.hpp"
#include <iostream>
#include <string>
#include <cstdio>
const std::string input = "data/raw_html/raw.txt";
const std::string root_path = "./wwwroot";
int main() {
ns_searcher::Searcher* search = new ns_searcher::Searcher();
search->InitSearcher(input);
std::string query;
std::string json_string;
char buffer[1024];
while (1) {
std::cout << "Enter Search Query: ";
// 使用 fgets 保留空格
fgets(buffer, sizeof(buffer) - 1, stdin);
// 去除末尾换行符
if (strlen(buffer) > 0) {
buffer[strlen(buffer) - 1] = 0;
}
query = buffer;
search->Search(query, &json_string);
std::cout << json_string << std::endl;
}
return 0;
}
这个循环结构允许用户连续进行多次搜索,直到手动中断程序。整体流程清晰,便于扩展后续的搜索算法逻辑。


