You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++读取txt日志到自定义对象并存入MySQL数据库

ATM日志解析入库落地实现方案

1. 核心实现逻辑拆分

整个流程按顺序拆为3个独立模块,和你现有代码结构完全兼容,不需要改动已完成的类、结构体定义:

  • 日志分块:按固定分隔符把整份txt拆成单条事件的独立文本块
  • 字段解析:遍历每个文本块,提取字段值赋值给events结构体,填充到Journal对象的trans_events集合
  • 安全入库:用MySQL预处理语句批量写入全量字段,避免SQL注入和语法错误

2. 模块实现代码

所有代码可以直接写到你现有的file.cpp中,补充对应的函数声明到header.h即可。

2.1 通用工具函数

先实现字符串去空格逻辑,避免日志前后的换行、空格导致字段匹配失败:

#include <fstream>
#include <vector>
#include <string>
#include <cctype>
#include <cstring>
#include <mysql/mysql.h>

std::string trim(const std::string& s) {
    auto start = s.begin();
    while (start != s.end() && std::isspace(static_cast<unsigned char>(*start))) start++;
    auto end = s.rbegin();
    while (end != s.rend() && std::isspace(static_cast<unsigned char>(*end))) end++;
    return std::string(start, end.base());
}

2.2 日志分块函数

逐行读取日志,遇到分隔符就切分独立事件块,自动跳过空块:

const std::string LOG_DELIMITER = "************************";

std::vector<std::vector<std::string>> split_log_blocks(std::ifstream& log_file) {
    std::vector<std::vector<std::string>> all_blocks;
    std::vector<std::string> current_block;
    std::string line;

    while (std::getline(log_file, line)) {
        std::string trimmed_line = trim(line);
        if (trimmed_line == LOG_DELIMITER) {
            if (!current_block.empty()) {
                all_blocks.push_back(current_block);
                current_block.clear();
            }
            continue;
        }
        current_block.push_back(trimmed_line);
    }
    // 处理文件末尾没有尾随分隔符的最后一个事件块
    if (!current_block.empty()) all_blocks.push_back(current_block);
    return all_blocks;
}

2.3 字段解析函数

遍历单个事件块的每一行,按字段名匹配提取值,填充events结构体:

events parse_single_event(const std::vector<std::string>& event_block) {
    events ev{}; // 结构体默认初始化
    for (const auto& line : event_block) {
        // 按你日志实际的字段分隔符调整,这里默认是key: value格式,如果是key=value就把':'改成'='
        size_t split_pos = line.find(':');
        if (split_pos == std::string::npos) continue;
        std::string key = trim(line.substr(0, split_pos));
        std::string val = trim(line.substr(split_pos + 1));

        // 以下字段匹配规则和你定义的events结构体一一对应,缺的字段按相同格式补全即可
        if (key == "id_event") ev.id_event = std::stoi(val);
        else if (key == "id_event_ligne") ev.id_event_ligne = std::stoi(val);
        else if (key == "amount_withdraw") ev.amount_withdraw = std::stod(val);
    }
    return ev;
}

2.4 MySQL安全入库函数

替换你原来硬编码单字段调用mysql_query的逻辑,用预处理语句自动处理转义,支持批量插入:

bool save_journal_to_db(MYSQL* mysql_conn, const Journal& journal) {
    // SQL字段顺序必须和你的journal_contents表结构完全一致,?是预处理占位符
    const char* insert_sql = R"(
        INSERT INTO journal_contents
        (id, id_atm, id_ligne, id_event, id_event_ligne, amount_withdraw)
        VALUES (?, ?, ?, ?, ?, ?)
    )";
    MYSQL_STMT* stmt = mysql_stmt_init(mysql_conn);
    if (!stmt) return false;
    if (mysql_stmt_prepare(stmt, insert_sql, strlen(insert_sql))) {
        mysql_stmt_close(stmt);
        return false;
    }

    // 开启事务,批量插入效率比单条提交高10倍以上
    mysql_query(mysql_conn, "START TRANSACTION");
    const int PARAM_COUNT = 6; // 按实际字段总数修改
    MYSQL_BIND bind[PARAM_COUNT];
    memset(bind, 0, sizeof(bind));

    // 绑定Journal类的固定公共属性
    bind[0].buffer_type = MYSQL_TYPE_LONG;
    bind[0].buffer = (void*)&journal.id;
    bind[1].buffer_type = MYSQL_TYPE_LONG;
    bind[1].buffer = (void*)&journal.id_atm;
    bind[2].buffer_type = MYSQL_TYPE_LONG;
    bind[2].buffer = (void*)&journal.id_ligne;

    // 遍历所有事件逐行绑定插入
    for (const auto& ev : journal.trans_events) {
        bind[3].buffer_type = MYSQL_TYPE_LONG;
        bind[3].buffer = (void*)&ev.id_event;
        bind[4].buffer_type = MYSQL_TYPE_LONG;
        bind[4].buffer = (void*)&ev.id_event_ligne;
        bind[5].buffer_type = MYSQL_TYPE_DOUBLE;
        bind[5].buffer = (void*)&ev.amount_withdraw;
        // 字符串类型字段绑定示例:bind[i].buffer_type = MYSQL_TYPE_STRING; bind[i].buffer = (void*)str_val.c_str(); bind[i].buffer_length = str_val.size();

        if (mysql_stmt_bind_param(stmt, bind)) {
            mysql_query(mysql_conn, "ROLLBACK");
            mysql_stmt_close(stmt);
            return false;
        }
        if (mysql_stmt_execute(stmt)) {
            mysql_query(mysql_conn, "ROLLBACK");
            mysql_stmt_close(stmt);
            return false;
        }
    }

    mysql_query(mysql_conn, "COMMIT");
    mysql_stmt_close(stmt);
    return true;
}

3. 流程串联

在你现有打开日志文件的逻辑后面,替换原来硬编码查找8902行的测试代码即可:

  1. 调用split_log_blocks拿到所有事件块
  2. 实例化Journal对象,给id、id_atm、id_ligne等公共属性赋值(去掉硬编码的测试ID,从配置、日志文件名等位置读取真实值)
  3. 遍历所有事件块,调用parse_single_event解析出events对象,push到journal.trans_events集合中
  4. 调用save_journal_to_db完成全量数据入库
  5. 关闭文件句柄、释放MySQL连接资源

4. 注意事项

  • 所有std::stoi、std::stod调用外层加try-catch,遇到格式异常的字段打错误日志标记位置,不要让整个程序崩溃
  • 单日志文件超过100M时,不要把所有事件块存在vector中,读一块、解析一块、入库一块,降低内存占用
  • 预处理语句会自动处理特殊字符转义,不要手动拼接SQL字符串,避免语法错误和SQL注入风险
  • 如果日志编码是GBK,入库前统一转成UTF-8,避免中文乱码

内容的提问来源于stack exchange,提问作者Hajar lyoubi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 02:48:26