当前位置: 首页 > news >正文

借助DeepSeek编写输出漂亮表格的chdb客户端

chdb是Auxten Wang创作的clickhouse的嵌入式引擎,它支持python、rust和c/c++语言调用动态链接库。
但官方没有给出c/c++语言调用的例子,在网友无敌大泡泡 的C语言示例启发下,我让DeepSeek改写出了一个简易客户端,
提示词如下:

附件是调用chdb动态链接库的c程序示例,请把它改写成支持输出表格(用|分隔各列,-分隔列标题和数据)和循环接收命令的客户端,.timer on/off命令用来开关计时,.exit用来退出,如果结果集大于10行,输出前后共10行,在中间用…表示,并打印实际行数。用c++实现

第一版的程序有个问题,输出表格缺列标题,误把第一行数据填在列标题的位置,反馈给他后,修改还是原样,于是考虑是否调用函数的结果集就是没有表头。结果在rust示例中看到了format.rs包含种类繁多的输出格式,其中有常见的PrettyCompact,把它和头文件一起提供给DeepSeek, 告诉他Pretty开头的格式原样输出即可。DeepSeek还发现format.rs中有CSVWithNames格式,聪明地改用它来获取表头,输出自定义表格。

首先下载动态链接库压缩包, 解压缩到/par目录。
然后编写c++源文件chsql.cpp,也放在/par目录下,最终的代码如下:

#include <iostream>
#include <string>
#include <vector>
#include <chrono>
#include <algorithm>
#include <iomanip>
#include <map>
#include "chdb.h"class ChDBClient {
private:chdb_connection* conn;bool timer_enabled;std::vector<std::string> history;std::string current_format;std::map<std::string, std::string> format_aliases;void initialize_format_aliases() {format_aliases = {{"csv", "CSVWithNames"},{"json", "JSONEachRow"},{"pretty", "Pretty"},{"vertical", "Vertical"},{"markdown", "Markdown"},{"tsv", "TabSeparatedWithNames"},{"table", "Pretty"}  // Alias for Pretty};}chdb_result* execute_query(const std::string& query) {auto start = std::chrono::high_resolution_clock::now();chdb_result* result = chdb_query(*conn, query.c_str(), current_format.c_str());auto end = std::chrono::high_resolution_clock::now();if (timer_enabled) {auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);std::cout << "Query executed in " << duration.count() << " ms\n";}return result;}void print_result(chdb_result* result) {// Check for errorsconst char* error = chdb_result_error(result);if (error) {std::cerr << "Query error: " << error << "\n";return;}// Process resultschar* data = chdb_result_buffer(result);size_t data_len = chdb_result_length(result);size_t rows_read = chdb_result_rows_read(result);double elapsed = chdb_result_elapsed(result);if (data_len > 0) {std::string result_data(data, data_len);std::cout << result_data;if (timer_enabled) {std::cout << "\n(" << rows_read << " rows, " << elapsed << " sec)\n";} else if (rows_read > 0) {std::cout << "\n(" << rows_read << " rows)\n";}} else {std::cout << "Query executed successfully. No results returned.\n";}}void print_table(const std::vector<std::vector<std::string>>& rows, const std::vector<std::string>& headers) {if (rows.empty()) return;// Calculate column widths based on both headers and datastd::vector<size_t> col_widths(headers.size(), 0);for (size_t i = 0; i < headers.size(); ++i) {col_widths[i] = headers[i].size();}for (const auto& row : rows) {for (size_t i = 0; i < row.size() && i < col_widths.size(); ++i) {col_widths[i] = std::max(col_widths[i], row[i].size());}}// Print header separatorstd::string header_sep = "+";for (size_t width : col_widths) {header_sep += std::string(width + 2, '-') + "+";}std::cout << header_sep << "\n";// Print headerstd::cout << "|";for (size_t i = 0; i < headers.size(); ++i) {std::cout << " " << std::left << std::setw(col_widths[i]) << headers[i] << " |";}std::cout << "\n" << header_sep << "\n";// Print rows (with potential truncation)size_t total_rows = rows.size();bool truncated = false;size_t print_count = total_rows;if (total_rows > 10) {print_count = 10;truncated = true;}// Print first 5 rowsfor (size_t i = 0; i < std::min(print_count / 2, total_rows); ++i) {print_row(rows[i], col_widths);}// Print ellipsis if truncatedif (truncated) {std::cout << "|";for (size_t i = 0; i < col_widths.size(); ++i) {std::cout << " " << std::left << std::setw(col_widths[i]) << "..." << " |";}std::cout << "\n";}// Print last 5 rows if truncatedif (truncated) {for (size_t i = std::max(total_rows - print_count / 2, print_count / 2); i < total_rows; ++i) {print_row(rows[i], col_widths);}} else {// Print remaining rows if not truncatedfor (size_t i = print_count / 2; i < total_rows; ++i) {print_row(rows[i], col_widths);}}std::cout << header_sep << "\n";if (truncated) {std::cout << "(" << total_rows << " rows total, showing first and last 5 rows)\n";} else if (total_rows > 0) {std::cout << "(" << total_rows << " rows)\n";}}void print_row(const std::vector<std::string>& row, const std::vector<size_t>& col_widths) {std::cout << "|";for (size_t i = 0; i < row.size() && i < col_widths.size(); ++i) {std::cout << " " << std::left << std::setw(col_widths[i]) << row[i] << " |";}std::cout << "\n";}// Parse CSV results and extract headers from column namesvoid parse_csv(const std::string& csv_data, std::vector<std::string>& headers, std::vector<std::vector<std::string>>& rows) {headers.clear();rows.clear();std::vector<std::string> current_row;std::string current_field;bool in_quotes = false;bool is_first_row = true;for (char c : csv_data) {if (c == '"') {in_quotes = !in_quotes;} else if (c == ',' && !in_quotes) {current_row.push_back(current_field);current_field.clear();} else if (c == '\n' && !in_quotes) {current_row.push_back(current_field);if (is_first_row) {headers = current_row;is_first_row = false;} else {rows.push_back(current_row);}current_row.clear();current_field.clear();} else {current_field += c;}}// Add the last field if not emptyif (!current_field.empty()) {current_row.push_back(current_field);}if (!current_row.empty()) {if (is_first_row) {headers = current_row;} else {rows.push_back(current_row);}}}public:ChDBClient() : conn(nullptr), timer_enabled(false), current_format("CSVWithNames") {initialize_format_aliases();}~ChDBClient() {if (conn) {chdb_close_conn(conn);}}bool connect() {char *argv[] = {"chdb_client","--path=:memory:"};int argc = 2;conn = chdb_connect(argc, argv);if (!conn) {std::cerr << "Failed to create database connection\n";return false;}std::cout << "Connected to in-memory database successfully\n";return true;}void run() {std::cout << "chDB client (type '.exit' to quit, '.help' for help)\n";while (true) {std::string input;std::cout << "chdb> ";std::getline(std::cin, input);// Trim whitespaceinput.erase(0, input.find_first_not_of(" \t\n\r\f\v"));input.erase(input.find_last_not_of(" \t\n\r\f\v") + 1);if (input.empty()) continue;// Add to historyhistory.push_back(input);// Handle meta commandsif (input[0] == '.') {if (input == ".exit") {break;} else if (input == ".timer on") {timer_enabled = true;std::cout << "Query timer enabled\n";continue;} else if (input == ".timer off") {timer_enabled = false;std::cout << "Query timer disabled\n";continue;} else if (input.substr(0, 8) == ".format ") {set_format(input.substr(8));continue;} else if (input == ".help") {print_help();continue;} else if (input == ".history") {print_history();continue;} else {std::cerr << "Unknown command: " << input << "\n";continue;}}// Execute SQL querychdb_result* result = execute_query(input);if (!result) {std::cerr << "Query execution failed\n";continue;}// For Pretty format, just print the result directlyif (current_format.substr(0, 6) == "Pretty" || current_format == "Vertical" || current_format == "Markdown") {print_result(result);} else {// For other formats, parse and display as tablechar* data = chdb_result_buffer(result);size_t data_len = chdb_result_length(result);if (data_len > 0) {std::string result_data(data, data_len);std::vector<std::string> headers;std::vector<std::vector<std::string>> rows;parse_csv(result_data, headers, rows);if (!headers.empty()) {print_table(rows, headers);} else {std::cout << result_data;}size_t rows_read = chdb_result_rows_read(result);double elapsed = chdb_result_elapsed(result);if (timer_enabled) {std::cout << "(" << rows_read << " rows, " << elapsed << " sec)\n";} else if (rows_read > 0) {std::cout << "(" << rows_read << " rows)\n";}} else {std::cout << "Query executed successfully. No results returned.\n";}}chdb_destroy_query_result(result);}}void set_format(const std::string& format) {std::string fmt_lower = format;std::transform(fmt_lower.begin(), fmt_lower.end(), fmt_lower.begin(), ::tolower);if (format_aliases.find(fmt_lower) != format_aliases.end()) {current_format = format_aliases[fmt_lower];std::cout << "Output format set to: " << current_format << "\n";} else {// Try to use the format directly if it's not an aliascurrent_format = format;std::cout << "Output format set to: " << current_format << "\n";}}void print_help() {std::cout << "Available commands:\n"<< "  .exit          - Exit the client\n"<< "  .timer on/off  - Enable/disable query timing\n"<< "  .format <fmt>  - Set output format (csv, json, pretty, vertical, markdown, tsv)\n"<< "  .history       - Show command history\n"<< "  .help          - Show this help message\n"<< "  SQL queries    - Execute SQL queries\n";}void print_history() {std::cout << "Command history:\n";for (size_t i = 0; i < history.size(); ++i) {std::cout << "  " << i + 1 << ": " << history[i] << "\n";}}
};int main() {ChDBClient client;if (!client.connect()) {return EXIT_FAILURE;}client.run();std::cout << "Goodbye!\n";return EXIT_SUCCESS;
}

编译命令行如下:

export LD_LIBRARY_PATH=/par:/usr/local/lib
export LIBRARY_PATH=/par:/usr/local/lib
g++ chsql4.cpp -O3 -o chsql4 -I . -lchdb

目前的代码还有冗余,比如计时信息和行数输出了两遍,下次慢慢改

 ./chsql
Connected to in-memory database successfully
chDB client (type '.exit' to quit, '.help' for help)
chdb> .format PrettyCompactMonoBlock
Output format set to: PrettyCompactMonoBlock
chdb> select * from file('foods.csv')limit 4;┌─category───┬─calories─┬─fats_g─┬─sugars_g─┐
1. │ vegetables │       450.522. │ seafood    │      150503. │ meat       │      100504. │ fruit      │       60011 │└────────────┴──────────┴────────┴──────────┘(4 rows)
chdb> .timer on
Query timer enabled
chdb> .format CSVWithNames
Output format set to: CSVWithNames
chdb> select * from file('foods.csv');
Query executed in 24 ms
+------------+----------+--------+----------+
| category   | calories | fats_g | sugars_g |
+------------+----------+--------+----------+
| vegetables | 45       | 0.5    | 2        |
| seafood    | 150      | 5      | 0        |
| meat       | 100      | 5      | 0        |
| fruit      | 60       | 0      | 11       |
| seafood    | 140      | 5      | 1        |
| ...        | ...      | ...    | ...      |
| seafood    | 130      | 1.5    | 0        |
| fruit      | 130      | 0      | 25       |
| meat       | 100      | 7      | 0        |
| vegetables | 30       | 0      | 5        |
| fruit      | 50       | 0      | 11       |
+------------+----------+--------+----------+
(27 rows total, showing first and last 5 rows)
(27 rows, 0.0232782 sec)
http://www.dtcms.com/a/278273.html

相关文章:

  • 电流驱动和电压驱动的区别
  • Rust语言实战:LeetCode算法精解
  • iOS高级开发工程师面试——RunLoop
  • FRP Ubuntu 服务端 + MacOS 客户端配置
  • 安装llama-factory报错 error: subprocess-exited-with-error
  • Kubernetes的微服务
  • c语言内存函数以及数据在内存中的存储
  • linux 文件搜索与文件内容查看
  • 嵌入式硬件篇---单稳态多谐施密特电路
  • CISSP通过回顾
  • Java Integer包装类缓存机制详解
  • uni-app 选择国家区号
  • AI与基础设施
  • 微服务引擎 MSE 及云原生 API 网关 2025 年 3 月产品动态
  • 基于光栅传感器+FPGA+ARM的测量控制解决方案
  • Quasar组件库
  • 2025最新android面试指南
  • vue3+arcgisAPI4示例:自定义多个气泡窗口展示(附源码下载)
  • [源力觉醒 创作者计划]_文心大模型4.5开源部署指南:从技术架构到实战落地
  • 知天命的感悟
  • 深入了解linux系统—— 进程信号的保存
  • 1688商品详情接口逆向分析与多语言SDK封装实践
  • Datawhale AI夏令营-基于带货视频评论的用户洞察挑战赛
  • 大数据领域开山鼻祖组件Hadoop核心架构设计
  • 某ctv视频下载逆向思路
  • 【Linux操作系统】简学深悟启示录:Linux权限
  • 算法第三十四天--动态规划part02(第九章)
  • fpga调试经验
  • 2025年渗透测试面试题总结-2025年HW(护网面试) 45(题目+回答)
  • EVOLVEpro安装使用教程-蛋白质语言模型驱动的快速定向进化