引言

在科学计算和工程应用领域,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_PARARUNTIME_ERRORIO_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.hppCSV/TSV/JSON 读写、过滤排序dsv_table 教程
配置解析get_option.hpp声明式参数管理、校验get_option 教程
进度条progress_bar.hpp终端进度条、异步动画progress_bar 教程
进程监控process_monitor.h暂停/继续/状态/安全退出process_monitor 教程
TOMLtoml.hppTOML 配置文件解析toml11 教程
时间utc_time.hppUTC 时间、儒略日、格式化本文 3.6
常数constants.hpp数学/地球物理常数本文 3.7
终端term_utils.hpp终端尺寸、光标控制、颜色本文 3.5
JSONjson.hppJSON 解析(nlohmann/json)本文 3.8
CLICLI11.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)
机构:浙江大学地球科学学院