引言
在科学计算和工程应用领域,C++ 开发者常常面临一个尴尬的局面:标准库提供了基础的数据结构和算法,但面对具体的科研需求——比如读取一个带注释的 CSV 文件、在终端显示一个进度条、解析复杂的配置文件、或者监控一个耗时数小时的计算进程——往往需要自行编写大量"胶水代码"。
UtilsXX 正是为解决这些痛点而生。它是一个基于 C++17 的轻量级工具库,专为地球科学、物理学和工程计算领域设计。不同于 Boost 这样的"全能型"库,UtilsXX 聚焦于科研工作者日常最高频的需求:数据处理、文件读写、终端交互、配置管理、时间计算和物理常数。本文将全面介绍 UtilsXX 的设计理念、核心模块和使用方法,帮助你快速上手并融入自己的项目。
一、设计理念:为什么创建 UtilsXX?
1.1 科研编程的特殊需求
科学计算程序与传统软件工程有显著不同:
- 数据驱动:大量时间花在读取、清洗、转换各种格式的数据上
- 长时运行:数值模拟可能持续数小时甚至数天,需要进度反馈和交互控制
- 参数密集:算法通常有数十个可调参数,需要灵活的配置管理
- 跨平台协作:代码需要在个人笔记本、工作站和超算集群上无缝运行
- 快速迭代:研究思路变化快,代码需要易于修改和扩展
1.2 UtilsXX 的设计原则
| 原则 | 实践 |
|---|---|
| Header-Only 优先 | 大部分组件为单头文件,直接 #include 即可使用 |
| 零依赖或内嵌依赖 | 核心模块无外部依赖;第三方库(如 nlohmann/json、toml11)直接内嵌 |
| 类型安全 | 大量使用模板和 static_assert,编译期捕获类型错误 |
| 异常友好 | 统一的 error_handler 错误处理机制,提供清晰的错误信息 |
| 跨平台 | Windows、Linux、macOS 统一封装,终端操作透明适配 |
| 科研导向 | 内置地球物理常数、WGS84 参数、角度弧度转换等地学常用工具 |
1.3 与现有生态的关系
UtilsXX 不是要取代谁,而是填补"标准库不够用、重型框架太重"之间的空白:
- vs 标准库:补充了 CSV/JSON/TOML 解析、进度条、终端控制等缺失功能
- vs Boost:更轻量、更聚焦,学习成本更低
- vs Python 生态:让你在 C++ 中也能拥有类似 pandas(
dsv_table)、tqdm(progress_bar)、argparse(get_option/CLI11)的体验
二、项目概览与快速开始
2.1 目录结构
utilsxx/
├── lib/ # 核心头文件库
│ ├── vector.hpp # 向量类型与算法
│ ├── matrix.hpp # 轻量级矩阵
│ ├── dsv_table.hpp # 表格数据处理
│ ├── str_utils.hpp # 字符串工具
│ ├── file_utils.hpp # 文件操作
│ ├── get_option.hpp # 配置参数解析
│ ├── progress_bar.hpp # 终端进度条
│ ├── process_monitor.h # 进程监控
│ ├── utc_time.hpp # UTC 时间处理
│ ├── constants.hpp # 数学与物理常数
│ ├── term_utils.hpp # 终端控制
│ ├── json.hpp # JSON 解析(nlohmann/json)
│ ├── toml.hpp # TOML 解析(toml11)
│ ├── CLI11.hpp # 命令行解析(CLI11)
│ ├── indicators.hpp # 高级进度条(indicators)
│ ├── error_handler.hpp # 错误处理
│ └── src/ # 少量需要编译的源文件
│ └── process_monitor.cpp
├── demo/ # 示例程序
├── data/ # 示例数据文件
├── extra/ # 第三方库示例
├── CMakeLists.txt # CMake 构建配置
├── manager.sh # 便捷管理脚本
└── UtilsXXConfig.cmake.in # CMake 包配置模板
2.2 编译安装
UtilsXX 使用 CMake 构建,支持作为子项目或直接安装到系统:
# 方式1:快速编译
mkdir build && cd build
cmake ..
make -j$(nproc)
# 方式2:使用管理脚本
./manager.sh configure # 配置
./manager.sh build # 编译
./manager.sh install # 安装
./manager.sh clean # 清理
# 方式3:作为 CMake 子项目
add_subdirectory(utilsxx)
target_link_libraries(your_target utilsxx)
可选依赖:
UTILSXX_USE_ARMADILLO=ON— 启用 Armadillo 线性代数封装UTILSXX_USE_EIGEN3=ON— 启用 Eigen 矩阵库封装
2.3 第一个程序
#include "utilsxx/lib/vector.hpp"
#include "utilsxx/lib/vector_operators.hpp"
#include "utilsxx/lib/constants.hpp"
#include <iostream>
int main() {
// 向量运算
utilsxx::vector1d a = {1.0, 2.0, 3.0};
utilsxx::vector1d b = {4.0, 5.0, 6.0};
auto c = a + b; // 元素级加法
// 使用物理常数
double circumference = 2 * utilsxx::UX_PI * utilsxx::UX_Earth_Radius;
std::cout << "c = " << c << std::endl;
std::cout << "Earth circumference ≈ " << circumference / 1000 << " km" << std::endl;
return 0;
}
三、核心模块详解
3.1 向量与矩阵(vector.hpp / matrix.hpp)
UtilsXX 提供了科研中最常用的数据结构的类型别名和算法:
类型别名系统:
// 一维向量
utilsxx::vector1d data; // std::vector<double>
utilsxx::vector1i indices; // std::vector<int>
utilsxx::vector1s labels; // std::vector<std::string>
utilsxx::vector1cd complex; // std::vector<std::complex<double>>
// 二维向量
utilsxx::vector2d grid; // std::vector<std::vector<double>>
// 矩阵(内存连续的二维数组)
utilsxx::matrix2d mat(100, 100, 0.0); // 100x100 零矩阵
向量算法:
utilsxx::vector1d v = {3.0, 4.0};
// 范数计算
double l2 = utilsxx::module(v, utilsxx::L2); // 5.0
double l1 = utilsxx::module(v, utilsxx::L1); // 7.0
double linf = utilsxx::module(v, utilsxx::Linf); // 4.0
// 归一化
utilsxx::normalize(v); // v 变为 {0.6, 0.8}
// 正交化
utilsxx::orth(a, b); // 将 b 投影到 a 的正交方向
// 范围约束
utilsxx::set2range(v, 0.0, 1.0, utilsxx::CutOff); // 截断
utilsxx::set2range(v, 0.0, 1.0, utilsxx::HardScale); // 线性映射
// 随机数生成
utilsxx::random_float(v, 0.0, 1.0, utilsxx::RdUniform); // 均匀分布
utilsxx::random_float(v, 0.0, 1.0, utilsxx::RdNormal); // 正态分布
utilsxx::random_int(v, 1, 100); // 整数均匀分布
// 序列生成
utilsxx::sequence(v, 0.0, 0.1); // 0.0, 0.1, 0.2, ...
// 函数式操作
auto squared = utilsxx::extract<double>(v, [](double x) { return x * x; });
utilsxx::for_each(v, [](double& x) { x *= 2; });
// 数组操作
auto sub = utilsxx::slice(v, 10, 20); // 提取子数组
auto combined = utilsxx::concat(a, b); // 连接数组
utilsxx::assign(v, 0.0, 50, 10); // 区间赋值
// 内存管理
utilsxx::destroy_vector(v); // 强制释放内存(swap 技巧)
矩阵操作:
utilsxx::matrix2d m(3, 3, 1.0); // 3x3 全1矩阵
m(1, 1) = 5.0; // 元素访问
auto row = m.row(0); // 提取行
auto col = m.col(1); // 提取列
auto mt = m.transpose(); // 转置
// 兼容 BLAS/LAPACK
double* ptr = m.data(); // 获取原始数据指针
// 支持行优先和列优先
utilsxx::matrix2d a(100, 100, utilsxx::RowMajor);
utilsxx::matrix2d b(100, 100, utilsxx::ColMajor);
3.2 字符串工具(str_utils.hpp)
字符串与数值的相互转换是数据处理的日常:
// 类型转字符串
std::string s = utilsxx::type2str(3.14159); // "3.14159"
// 字符串转类型(支持自定义分隔符)
double d;
utilsxx::str2type("3.14", d); // 标准转换
utilsxx::str2type("3/14", d, '/'); // 分隔符替换后转换
// 特殊 double 转换(支持 Fortran 格式)
double nan = utilsxx::str2double("NAN"); // NaN
double inf = utilsxx::str2double("INF"); // Inf
double sci = utilsxx::str2double("1.5D+10"); // Fortran 科学计数法
// 字符串解析为向量
std::vector<double> vals;
utilsxx::parse_string_to_vector("1.0 2.0 3.0", ' ', vals);
utilsxx::parse_string_to_vector("1.0,2.0,3.0", ',', vals);
// CSV 行解析(支持引号包裹字段)
std::vector<std::string> fields;
utilsxx::parse_csv_line("name,\"enshi, hubei\",age", fields);
// 结果: ["name", "enshi, hubei", "age"]
// 多值解析
std::vector<std::string> tokens;
utilsxx::parse_string_with_quotes("cmd \"arg with spaces\" flag", tokens);
// 字符串替换
std::string result;
utilsxx::replace_all(result, "hello world", "world", "UtilsXX");
// 字符串拼接(避免重复后缀)
std::string path = utilsxx::patch_string("data/file", ".txt"); // "data/file.txt"
std::string same = utilsxx::patch_string("data/file.txt", ".txt"); // "data/file.txt"
3.3 表格数据处理(dsv_table.hpp)
dsv_table 是 UtilsXX 中最强大的数据处理组件之一,支持 CSV、TSV 和任意分隔符格式:
utilsxx::dsv_table table;
// 读取 CSV(自动识别列头)
table.load_csv("data/sample_data");
// 读取自定义分隔符文件
table.delimeter('|');
table.head_number(1);
table.load_text("data/world_data", ".txt", utilsxx::ColHead | utilsxx::RowHead);
// 数据访问(1-based,cell 为 0-based)
std::string name = table.cell<std::string>(1, 0); // 第1行行名
std::vector<double> lon = table.get_column<double>("lon");
std::vector<double> row2 = table.get_row<double>(2);
// 过滤与排序
table.filter("America", "Continent_s", utilsxx::ColHead); // 正则过滤
table.reorder<int>("SurfaceArea_n", utilsxx::ASCENDING); // 排序
// 输出控制(软删除)
table.column_output("deprecated", utilsxx::Disable);
// 导出为 JSON
table.save_json("output", 0); // 对象数组格式
更多细节请参阅专门的 dsv_table 教程。
3.4 配置参数解析(get_option.hpp)
声明式配置管理,让参数解析变得简洁:
utilsxx::getoption gopt;
// 声明参数
gopt.add_options(
{"range", "interval", "weight", "model-file", "output"},
{true, true, false, true, false} // 是否必填
);
// 设置互斥组(至少选一个)
gopt.set_group(1, {"out-model", "save-model"});
// 读取配置
gopt.read_options("config.txt");
// 自动校验
gopt.check_mandatory();
gopt.check_groups();
// 获取值(自动类型转换)
std::string model = gopt.get_value<std::string>("model-file");
std::vector<double> w = gopt.get_values<double>("weight", '|', '/');
// 别名支持
auto v = gopt.get_value<std::string>("model-file|ModelFile|model_file");
3.5 终端交互(progress_bar.hpp / process_monitor.h / term_utils.hpp)
进度条:
utilsxx::progress_bar bar("Processing", 1000);
for (int i = 0; i < 1000; ++i) {
bar.tick();
// 你的计算...
}
进程监控(支持暂停/继续/状态查看):
class MySimulation : public utilsxx::process_monitor {
public:
void process() override {
while (!end_process()) {
compute_step();
increment_iteration();
wait_for_keyboard(1); // 每秒检查键盘
}
}
};
MySimulation sim;
sim.start_monitoring(); // 按 'p' 暂停,'c' 继续,'s' 查看状态,'q' 退出
终端控制:
int w = utilsxx::terminal_width(); // 获取终端宽度
int h = utilsxx::terminal_height(); // 获取终端高度
std::cout << TERM_WIN_BOLDRED << "Error!" << TERM_WIN_RESET << std::endl;
std::cout << TERM_WIN_CLEARALL; // 清屏
TERM_WIN_MOVETO(std::cout, 10, 20); // 移动光标到第10行第20列
3.6 时间处理(utc_time.hpp)
专为地球科学设计的时间结构体:
// 构造时间
utilsxx::UTC_TIME t1(2024, 5, 10, 14, 30, 0, 0);
utilsxx::UTC_TIME t2("2024-05-10T14:30:00.000");
utilsxx::UTC_TIME t3; t3.set_to_now(); // 当前 UTC 时间
// 时间运算
t1.add_duration(3600); // 增加1小时
double diff = t1.diff_sec(t2); // 时间差(秒)
t1.round_up_to(utilsxx::Second); // 向上取整到秒
// 儒略日转换
int julday;
utilsxx::julian_day(2024, 5, 10, julday); // 131
utilsxx::month_and_day(2024, 131, month, day); // 5, 10
// 格式化输出
std::string iso = t1.time_str(); // "2024-5-10T14:30:0.000"
std::string sym = t1.time_str(true); // "May. 10 2024 14:30:0.000"
std::string rdseed = t1.rdseed_time_str(); // "2024.131.14.30.00.0000"
3.7 物理常数(constants.hpp)
内置地球科学常用常数:
// 数学常数
utilsxx::UX_PI // π
utilsxx::UX_DEG2ARC // 角度转弧度 (π/180)
utilsxx::UX_ARC2DEG // 弧度转角度 (180/π)
utilsxx::UX_GoldenMean // 黄金比例
// 地球参数
utilsxx::UX_Earth_Radius // 6371008.8 m
utilsxx::UX_Earth_GravRadius // 6378136.46 m(重力场模型)
utilsxx::UX_Earth_MagRadius // 6371200 m(地磁场模型)
utilsxx::UX_Earth_GM // 3.986004418e+14 m³/s²
utilsxx::UX_WGS84_EquatorRadius// 6378137 m
utilsxx::UX_WGS84_PoleRadius // 6356752.3142 m
utilsxx::UX_WGS84_FLAT // 扁率
// 其他天体
utilsxx::UX_Moon_Radius // 1738000 m
utilsxx::UX_Mars_Radius // 3389500 m
utilsxx::UX_Mars_EquatorRadius // 3395428 m
// 物理常数
utilsxx::UX_G0 // 万有引力常数 6.67408e-11
utilsxx::UX_MU0 // 真空磁导率
utilsxx::UX_EPS0 // 真空介电常数
utilsxx::UX_T0 // 地磁场平均强度 5e+4 nT
3.8 第三方库集成
UtilsXX 内嵌了多个优秀的第三方库,方便统一使用:
JSON 解析(nlohmann/json):
#include "json.hpp"
nlohmann::json j = nlohmann::json::parse("{\"name\": \"test\"}");
std::string name = j["name"];
TOML 解析(toml11):
#include "toml.hpp"
auto root = toml::parse("config.toml");
std::string title = toml::find<std::string>(root, "title");
命令行解析(CLI11):
#include "CLI11.hpp"
CLI::App app("My Program");
std::string config;
app.add_option("-c,--config", config, "Config file");
CLI11_PARSE(app, argc, argv);
高级进度条(indicators):
#include "indicators.hpp"
indicators::ProgressBar bar{
indicators::option::BarWidth{50},
indicators::option::Fill{"🔥"}
};
四、错误处理机制
UtilsXX 使用统一的异常体系:
try {
utilsxx::dsv_table table;
table.load_csv("nonexistent.csv");
}
catch (const utilsxx::error_handler& e) {
e.show(); // 打印格式化的错误信息
}
error_handler 包含:
- 错误代码(
INVALID_PARA、RUNTIME_ERROR、IO_ERROR等) - 发生错误的函数名(
__PRETTY_FUNCTION__) - 详细的错误描述
五、完整项目示例
以下是一个典型的地球物理数据处理流程,展示了多个 UtilsXX 模块的协同使用:
#include "utilsxx/lib/get_option.hpp"
#include "utilsxx/lib/dsv_table.hpp"
#include "utilsxx/lib/progress_bar.hpp"
#include "utilsxx/lib/utc_time.hpp"
#include "utilsxx/lib/constants.hpp"
#include <iostream>
int main() try {
// 1. 读取配置
utilsxx::getoption gopt;
gopt.add_options({"input", "output", "column", "threshold"},
{true, true, true, false});
gopt.read_options("config.txt");
gopt.check_mandatory();
std::string input = gopt.get_value<std::string>("input");
std::string output = gopt.get_value<std::string>("output");
std::string col_name = gopt.get_value<std::string>("column");
double threshold = gopt.has_value("threshold")
? gopt.get_value<double>("threshold") : 0.0;
// 2. 记录开始时间
utilsxx::UTC_TIME start; start.set_to_now();
std::cout << "Start: " << start.time_str() << std::endl;
// 3. 加载数据
utilsxx::dsv_table table;
table.load_csv(input);
std::cout << "Loaded " << table.row_number() << " rows, "
<< table.col_number() << " columns" << std::endl;
// 4. 处理数据(带进度条)
utilsxx::progress_bar bar("Processing", table.row_number());
for (int i = 1; i <= table.row_number(); ++i) {
double val = table.cell<double>(i, table.name_index(col_name));
if (val > threshold) {
table.cell(i, table.name_index(col_name), val * utilsxx::UX_DEG2ARC);
}
bar.tick();
}
// 5. 保存结果
table.save_csv(output);
// 6. 输出统计
utilsxx::UTC_TIME end; end.set_to_now();
std::cout << "End: " << end.time_str() << std::endl;
std::cout << "Elapsed: " << end.diff_sec(start) << " seconds" << std::endl;
return 0;
}
catch (const utilsxx::error_handler& e) {
e.show();
return 1;
}
六、模块速查表
| 模块 | 头文件 | 功能 | 对应教程 |
|---|---|---|---|
| 向量工具 | vector.hpp | 类型别名、范数、归一化、随机数、序列 | 本文 3.1 |
| 矩阵 | matrix.hpp | 轻量级矩阵、行列提取、转置、BLAS 兼容 | 本文 3.1 |
| 字符串 | str_utils.hpp | 类型转换、CSV 解析、字符串操作 | 本文 3.2 |
| 表格 | dsv_table.hpp | CSV/TSV/JSON 读写、过滤排序 | dsv_table 教程 |
| 配置解析 | get_option.hpp | 声明式参数管理、校验 | get_option 教程 |
| 进度条 | progress_bar.hpp | 终端进度条、异步动画 | progress_bar 教程 |
| 进程监控 | process_monitor.h | 暂停/继续/状态/安全退出 | process_monitor 教程 |
| TOML | toml.hpp | TOML 配置文件解析 | toml11 教程 |
| 时间 | utc_time.hpp | UTC 时间、儒略日、格式化 | 本文 3.6 |
| 常数 | constants.hpp | 数学/地球物理常数 | 本文 3.7 |
| 终端 | term_utils.hpp | 终端尺寸、光标控制、颜色 | 本文 3.5 |
| JSON | json.hpp | JSON 解析(nlohmann/json) | 本文 3.8 |
| CLI | CLI11.hpp | 命令行解析(CLI11) | 本文 3.8 |
七、贡献与许可
UtilsXX 基于 GNU LGPL v2.1 开源。欢迎提交 Issue 和 Pull Request。
项目由浙江大学地球科学学院的张壹(Yi Zhang)开发和维护。
结语
UtilsXX 不是一个追求"大而全"的框架,而是一个懂科研工作者痛点的实用工具箱。它将日常开发中最繁琐、最重复的工作封装成简洁的 API,让你可以把更多精力放在科学问题本身。无论你是处理地球物理观测数据、运行数值模拟,还是构建数据分析管道,UtilsXX 都能成为你可靠的 C++ 伙伴。
项目地址:utilsxx
作者:Yi Zhang (yizhang-geo@zju.edu.cn)
机构:浙江大学地球科学学院