[{"content":"引言 在科学计算、数据分析和工程应用中，表格数据（如 CSV、TSV、空格分隔文件）是最常见的数据格式之一。然而，C++ 标准库并未提供开箱即用的表格处理工具，开发者往往需要自己编写解析逻辑，处理分隔符、表头、注释、空值等琐碎问题。\ndsv_table 是 utilsxx 库中的一个核心组件，它提供了一套完整的表格数据读写与操作方案，支持多种分隔格式、灵活的行列访问、数据过滤与排序，以及 JSON 互操作。本文将带你从零开始，一文看懂 dsv_table 的设计理念与使用技巧。\n一、什么是 dsv_table？ dsv_table 中的 DSV 是 Delimiter-Separated Values 的缩写，泛指所有以分隔符（逗号、空格、竖线等）组织的文本表格格式。dsv_table 不仅能处理 CSV，还能处理任意自定义分隔符的文本文件。\n核心设计特点 特性 说明 统一存储 所有单元格内部以字符串存储，支持按需转换为 int、double、std::string 类型标记 每个单元格带有 String / Int / Float 类型标记，影响输出行为 输出控制 可单独控制行/列/单元格的输出开关（Enable/Disable），实现\u0026quot;软删除\u0026quot; 行列命名 支持通过名称（如 \u0026quot;SurfaceArea_n\u0026quot;）或内置编号（如 R3、C5）访问 多格式支持 原生支持 .txt、.csv、.json 的读写 头信息/注释/标记 文件中的注释行、标记行、头信息行会被自动提取并保留 二、快速上手 2.1 基本加载与查看 假设我们有一个 CSV 文件 sample_data.csv：\nid,lon,lat,depth,den,sus,name,date,location ,32.23,65.23,,2.3,,gabbro,,\u0026#34;enshi, hubei\u0026#34; ,65.2,12.7,,2.5,,basalt,,\u0026#34;wuhan\u0026#34; ,70.23,10.6,,3.1,,volcanic,,\u0026#34;hangzhou, zhejiang\u0026#34; 加载并查看列信息：\n#include \u0026#34;dsv_table.hpp\u0026#34; int main() { utilsxx::dsv_table t(\u0026#34;data/sample_data\u0026#34;, \u0026#34;.csv\u0026#34;); t.info(utilsxx::ColInfo); // 打印列信息 return 0; } 输出示例：\nColumns: id | Enabled | String | -\u0026gt; lon | Enabled | Float | 32.23 -\u0026gt; 70.23 lat | Enabled | Float | 65.23 -\u0026gt; 10.6 depth | Enabled | Float | -\u0026gt; den | Enabled | Float | 2.3 -\u0026gt; 3.1 sus | Enabled | Float | -\u0026gt; name | Enabled | String | gabbro -\u0026gt; volcanic date | Enabled | String | -\u0026gt; location | Enabled | String | enshi, hubei -\u0026gt; hangzhou, zhejiang ------------ 注意：load_csv() 默认认为第一行是列头（ColHead），所以 id, lon, lat... 被自动识别为列名称。\n2.2 自定义分隔符加载 对于竖线分隔的文件（如 world_data.txt），需要手动指定分隔符：\nutilsxx::dsv_table tc; tc.delimeter(\u0026#39;|\u0026#39;); // 设置分隔符为竖线 tc.head_number(1); // 指定有1行头信息 tc.load_text(\u0026#34;data/world_data\u0026#34;, \u0026#34;.txt\u0026#34;, utilsxx::ColHead | utilsxx::RowHead); 这里 ColHead | RowHead 表示既有列头，又有行头——第一行是列名称，第一列是行名称。\n三、文件格式详解 dsv_table 支持一种富文本表格格式，其规则如下：\n3.1 注释行（以 # 开头） # World Bank Population Dataset # Data is available at xxx.com 会被保存到 annotates_ 中，可通过 annotations() 获取。\n3.2 标记行（以 #! 开头） #! rows = 240 #! cols = 15 会被保存到 tags_ 中，可通过 tags() 获取。适合存放元数据。\n3.3 头信息行（不以 # 开头，且在数据之前） This is a test file for the 2014 World Bank population dataset. 通过 head_number(n) 设置前 n 行为头信息，会被保存到 heads_ 中。\n3.4 数据行 头信息之后的非空行即为数据。dsv_table 会自动：\n去除首尾空白 处理 CRLF 换行符 对 CSV 格式使用专用解析器（支持引号包裹的字段，如 \u0026quot;enshi, hubei\u0026quot;） 动态补齐缺失列（用空单元格填充） 四、数据访问与操作 4.1 索引访问 dsv_table 中有一个重要的索引约定需要牢记：\n除 cell() 外，所有行列操作均为 1-based（不直接操作行头/列头）；只有 cell(r, c) 是 0-based，可以访问包括表头在内的任意单元格。\n// cell() 是 0-based，可以操作表头 double val = t.cell\u0026lt;double\u0026gt;(1, 2); // 第2行第3列的数据（0-based） t.cell(0, 0, \u0026#34;CornerName\u0026#34;); // 修改左上角表头单元格 t.cell(0, 1, \u0026#34;ColumnA\u0026#34;); // 修改第1个列名 t.cell(1, 0, \u0026#34;Row1\u0026#34;); // 修改第1个行名 // 其他所有 API 都是 1-based，只操作数据区域 std::vector\u0026lt;double\u0026gt; col3 = t.get_column\u0026lt;double\u0026gt;(3); // 第3列数据 std::vector\u0026lt;double\u0026gt; row2 = t.get_row\u0026lt;double\u0026gt;(2); // 第2行数据 t.fill_column(data, 1); // 填充第1列数据 t.row_type(utilsxx::Int, 2); // 设置第2行类型 这种设计的好处是：\n1-based API（get_column、get_row、fill_column、reorder 等）专注于数据操作，默认隔离表头，避免误改 0-based cell() 提供底层细粒度控制，需要时可以直接修改行列名称或角落单元格 4.2 名称访问 // 通过列名获取一整列 std::vector\u0026lt;double\u0026gt; lon = t.get_column\u0026lt;double\u0026gt;(\u0026#34;lon\u0026#34;); // 通过行名获取一整行（world_data.txt 中第一列是行名） std::vector\u0026lt;std::string\u0026gt; china_row = t.get_row\u0026lt;std::string\u0026gt;(\u0026#34;CHN\u0026#34;); 4.3 内置编号访问 如果某行/列没有名称，可以使用内置格式 R\u0026lt;id\u0026gt; 和 C\u0026lt;id\u0026gt;：\n// 获取第5列（无论是否有列名） std::vector\u0026lt;double\u0026gt; col5 = t.get_column\u0026lt;double\u0026gt;(\u0026#34;C5\u0026#34;); // 获取第10行 std::vector\u0026lt;std::string\u0026gt; row10 = t.get_row\u0026lt;std::string\u0026gt;(\u0026#34;R10\u0026#34;); 五、行列操作 5.1 添加列 // 在末尾添加一个空白列，命名为 \u0026#34;new_col\u0026#34; int idx = t.add_column(\u0026#34;new_col\u0026#34;); // 在索引2的位置插入数据列 std::vector\u0026lt;double\u0026gt; new_data = {1.0, 2.0, 3.0}; t.add_column(2, new_data); // 在第二列前插入 // 通过列名定位插入 t.add_column(\u0026#34;after_this\u0026#34;, new_data); // 在 \u0026#34;after_this\u0026#34; 列后插入 5.2 添加行 // 添加空白行 int row_idx = t.add_row(\u0026#34;China\u0026#34;); // 插入数据行 std::vector\u0026lt;std::string\u0026gt; row_data = {\u0026#34;CHN\u0026#34;, \u0026#34;China\u0026#34;, \u0026#34;Asia\u0026#34;}; t.add_row(\u0026#34;new_row\u0026#34;, row_data); 5.3 填充与修改 // 填充指定列 std::vector\u0026lt;double\u0026gt; depths = {100.0, 200.0, 150.0}; t.fill_column(depths, \u0026#34;depth\u0026#34;); // 填充指定行 t.fill_row(row_data, \u0026#34;CHN\u0026#34;); 六、数据过滤与排序 6.1 按正则表达式过滤 filter() 支持按行或按列过滤，不符合条件的行列会被禁用输出（并非真正删除）：\n// 按列过滤：只保留 \u0026#34;Continent_s\u0026#34; 列中包含 \u0026#34;America\u0026#34; 的行 // 参数说明：正则表达式, 基准列/行名, 基准类型 tc.filter(\u0026#34;America\u0026#34;, \u0026#34;Continent_s\u0026#34;, utilsxx::ColHead); 这里 ColHead 表示 \u0026quot;Continent_s\u0026quot; 是一个列名，因此会按行过滤——只保留该列匹配 \u0026ldquo;America\u0026rdquo; 的行。\n6.2 自定义函数过滤 // 定义过滤函数：保留第一列（行头除外）大于100的行 bool filter_func(const std::vector\u0026lt;utilsxx::table_cell\u0026gt;\u0026amp; row) { return row[1].value\u0026lt;double\u0026gt;() \u0026gt; 100.0; } tc.filter(filter_func, utilsxx::RowHead); // 按行过滤 6.3 排序 // 按 \u0026#34;SurfaceArea_n\u0026#34; 列升序排序（整行联动） tc.reorder\u0026lt;int\u0026gt;(\u0026#34;SurfaceArea_n\u0026#34;, utilsxx::ASCENDING); // 按第3列降序排序 tc.reorder\u0026lt;double\u0026gt;(3, utilsxx::DESCENDING); reorder 是扩展模式排序，即排序时整行数据跟随排序键一起移动。\n七、输出控制：Enable / Disable dsv_table 的一大特色是输出开关机制。你可以禁用某些行或列，它们仍存在于内存中，但不会被保存到文件：\n// 禁用第3列 t.column_output(3, utilsxx::Disable); // 禁用名为 \u0026#34;deprecated\u0026#34; 的列 t.column_output(\u0026#34;deprecated\u0026#34;, utilsxx::Disable); // 禁用第5行 t.row_output(5, utilsxx::Disable); // 禁用整个表格 t.table_output(utilsxx::Disable); // 重新启用 t.table_output(utilsxx::Enable); 导出过滤后的表格 // 导出时忽略被禁用的行列（默认行为） utilsxx::dsv_table filtered = tc.export_table(); // 包含被禁用的行列 utilsxx::dsv_table full = tc.export_table(false); 八、JSON 互操作 dsv_table 支持 JSON 格式的读写，方便与现代 Web 流程对接。\n8.1 从 JSON 加载 支持两种格式：\n格式0 - 对象数组（默认）：\n[ {\u0026#34;姓名\u0026#34;: \u0026#34;张三\u0026#34;, \u0026#34;年龄\u0026#34;: 25, \u0026#34;城市\u0026#34;: \u0026#34;北京\u0026#34;, \u0026#34;分数\u0026#34;: 85.5}, {\u0026#34;姓名\u0026#34;: \u0026#34;李四\u0026#34;, \u0026#34;年龄\u0026#34;: 30, \u0026#34;城市\u0026#34;: \u0026#34;上海\u0026#34;, \u0026#34;分数\u0026#34;: 92.0} ] utilsxx::dsv_table t; t.load_json(\u0026#34;data/student_data\u0026#34;, 0); // 格式0 格式1 - 表格格式：\n{ \u0026#34;headers\u0026#34;: [\u0026#34;姓名\u0026#34;, \u0026#34;年龄\u0026#34;, \u0026#34;城市\u0026#34;, \u0026#34;分数\u0026#34;], \u0026#34;data\u0026#34;: [ [\u0026#34;张三\u0026#34;, 25, \u0026#34;北京\u0026#34;, 85.5], [\u0026#34;李四\u0026#34;, 30, \u0026#34;上海\u0026#34;, 92.0] ] } t.load_json(\u0026#34;data/table_data\u0026#34;, 1); // 格式1 8.2 保存为 JSON // 保存为对象数组格式（默认） t.save_json(\u0026#34;output\u0026#34;, 0); // 保存为表格格式 t.save_json(\u0026#34;output\u0026#34;, 1); 输出会自动根据单元格类型（Int/Float/String）生成对应的 JSON 类型，而非全部转为字符串。\n九、实用技巧 9.1 浮点数精度控制 // 设置 double 类型保存时的有效数字为4位 t.cell(1, 1, 3.1415926, 4); // 保存为 \u0026#34;3.142\u0026#34; // 批量填充时控制精度 std::vector\u0026lt;double\u0026gt; data = {1.234567, 2.345678}; t.fill_column(data, \u0026#34;value\u0026#34;, 3); // 保留3位有效数字 9.2 类型设置 // 将 \u0026#34;id\u0026#34; 列设为整数类型 t.column_type(utilsxx::Int, \u0026#34;id\u0026#34;); // 将第2行设为字符串类型 t.row_type(utilsxx::String, 2); 类型会影响 JSON 输出时的数据类型。\n9.3 检查行列是否存在 if (t.has_column(\u0026#34;depth\u0026#34;)) { // 处理 depth 列 } if (t.has_row(\u0026#34;CHN\u0026#34;)) { // 处理中国行 } 9.4 遍历表格（跳过表头） // begin() 自动跳过第0行（表头行） for (auto it = t.begin(); it != t.end(); ++it) { for (const auto\u0026amp; cell : *it) { std::cout \u0026lt;\u0026lt; cell.str_ \u0026lt;\u0026lt; \u0026#34; \u0026#34;; } std::cout \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } 9.5 移动语义优化 dsv_table 实现了移动构造和移动赋值，大数据量传递时无需深拷贝：\nutilsxx::dsv_table load_big_data() { utilsxx::dsv_table t; t.load_csv(\u0026#34;huge_file.csv\u0026#34;); return t; // 移动语义，高效 } auto data = load_big_data(); 十、完整示例 以下是一个综合示例，演示从加载到过滤、排序、保存的完整流程：\n#include \u0026#34;dsv_table.hpp\u0026#34; #include \u0026lt;iostream\u0026gt; int main() try { // 1. 加载竖线分隔的世界银行数据 utilsxx::dsv_table tc; tc.delimeter(\u0026#39;|\u0026#39;); tc.head_number(1); tc.load_text(\u0026#34;data/world_data\u0026#34;, \u0026#34;.txt\u0026#34;, utilsxx::ColHead | utilsxx::RowHead); // 2. 查看文件元信息 tc.info(utilsxx::AttInfo | utilsxx::HeadInfo | utilsxx::TagInfo); // 3. 过滤：只保留美洲国家 tc.filter(\u0026#34;America\u0026#34;, \u0026#34;Continent_s\u0026#34;, utilsxx::ColHead); // 4. 检查列是否存在 if (tc.has_column(\u0026#34;SurfaceArea_n\u0026#34;)) { std::cout \u0026lt;\u0026lt; \u0026#34;找到 SurfaceArea_n 列\\n\u0026#34;; } // 5. 导出过滤结果（生成新表格） utilsxx::dsv_table tc2 = tc.export_table(); // 6. 按国土面积升序排序 tc2.reorder\u0026lt;int\u0026gt;(\u0026#34;SurfaceArea_n\u0026#34;, utilsxx::ASCENDING); // 7. 设置分隔符并保存 tc2.delimeter(\u0026#39;|\u0026#39;); tc2.save_text(\u0026#34;america_sorted\u0026#34;); // 8. 同时保存为 JSON tc2.save_json(\u0026#34;america_sorted\u0026#34;, 0); return 0; } catch(const utilsxx::error_handler\u0026amp; e) { e.show(); return 1; } 十一、API 速查表 操作 方法 加载文本 load_text(filename, ext, head_type) 加载 CSV load_csv(filename, head_type) 加载 JSON load_json(filename, format) 保存文本 save_text(filename, ext) 保存 CSV save_csv(filename) 保存 JSON save_json(filename, format) 设置分隔符 delimeter(char) 获取列数据 get_column\u0026lt;T\u0026gt;(idx/name) 获取行数据 get_row\u0026lt;T\u0026gt;(idx/name) 获取单元格 cell\u0026lt;T\u0026gt;(r, c) 设置单元格 cell(r, c, value, precision) 填充列 fill_column(data, idx/name, precision) 填充行 fill_row(data, idx/name, precision) 添加列 add_column(name/idx, [data]) 添加行 add_row(name/idx, [data]) 设置列类型 column_type(type, idx/name) 设置行类型 row_type(type, idx/name) 过滤（正则） filter(regex, target, head_type) 过滤（函数） filter(func, head_type) 排序 reorder\u0026lt;T\u0026gt;(idx/name, order) 禁用列 column_output(idx/name, Disable) 禁用行 row_output(idx/name, Disable) 导出表格 export_table(ignore_disabled) 查看信息 info(mask, os) 结语 dsv_table 是一个设计精良、功能完整的 C++ 表格处理工具。它将文件解析、数据存储、类型转换、过滤排序、多格式导出整合在一个类中，既适合快速脚本式数据处理，也能胜任大型科学计算项目的数据预处理需求。\n如果你正在寻找一个比手动解析 CSV 更强大、比引入重型数据库更轻量的表格处理方案，dsv_table 值得一试。\n项目地址：utilsxx\n","permalink":"https://geowisdom.com.cn/posts/resource/utilsxx/readme_dsv_table/","summary":"\u003ch2 id=\"引言\"\u003e引言\u003c/h2\u003e\n\u003cp\u003e在科学计算、数据分析和工程应用中，表格数据（如 CSV、TSV、空格分隔文件）是最常见的数据格式之一。然而，C++ 标准库并未提供开箱即用的表格处理工具，开发者往往需要自己编写解析逻辑，处理分隔符、表头、注释、空值等琐碎问题。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003edsv_table\u003c/code\u003e 是 \u003ca href=\"https://github.com/geophyxx/utilsxx\"\u003eutilsxx\u003c/a\u003e 库中的一个核心组件，它提供了一套\u003cstrong\u003e完整的表格数据读写与操作方案\u003c/strong\u003e，支持多种分隔格式、灵活的行列访问、数据过滤与排序，以及 JSON 互操作。本文将带你从零开始，一文看懂 \u003ccode\u003edsv_table\u003c/code\u003e 的设计理念与使用技巧。\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"一什么是-dsv_table\"\u003e一、什么是 dsv_table？\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003edsv_table\u003c/code\u003e 中的 \u003cstrong\u003eDSV\u003c/strong\u003e 是 \u003cstrong\u003eDelimiter-Separated Values\u003c/strong\u003e 的缩写，泛指所有以分隔符（逗号、空格、竖线等）组织的文本表格格式。\u003ccode\u003edsv_table\u003c/code\u003e 不仅能处理 CSV，还能处理任意自定义分隔符的文本文件。\u003c/p\u003e\n\u003ch3 id=\"核心设计特点\"\u003e核心设计特点\u003c/h3\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e特性\u003c/th\u003e\n          \u003cth\u003e说明\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003e统一存储\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003e所有单元格内部以字符串存储，支持按需转换为 \u003ccode\u003eint\u003c/code\u003e、\u003ccode\u003edouble\u003c/code\u003e、\u003ccode\u003estd::string\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003e类型标记\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003e每个单元格带有 \u003ccode\u003eString\u003c/code\u003e / \u003ccode\u003eInt\u003c/code\u003e / \u003ccode\u003eFloat\u003c/code\u003e 类型标记，影响输出行为\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003e输出控制\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003e可单独控制行/列/单元格的输出开关（\u003ccode\u003eEnable\u003c/code\u003e/\u003ccode\u003eDisable\u003c/code\u003e），实现\u0026quot;软删除\u0026quot;\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003e行列命名\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003e支持通过名称（如 \u003ccode\u003e\u0026quot;SurfaceArea_n\u0026quot;\u003c/code\u003e）或内置编号（如 \u003ccode\u003eR3\u003c/code\u003e、\u003ccode\u003eC5\u003c/code\u003e）访问\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003e多格式支持\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003e原生支持 \u003ccode\u003e.txt\u003c/code\u003e、\u003ccode\u003e.csv\u003c/code\u003e、\u003ccode\u003e.json\u003c/code\u003e 的读写\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003e头信息/注释/标记\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003e文件中的注释行、标记行、头信息行会被自动提取并保留\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003chr\u003e\n\u003ch2 id=\"二快速上手\"\u003e二、快速上手\u003c/h2\u003e\n\u003ch3 id=\"21-基本加载与查看\"\u003e2.1 基本加载与查看\u003c/h3\u003e\n\u003cp\u003e假设我们有一个 CSV 文件 \u003ccode\u003esample_data.csv\u003c/code\u003e：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-csv\" data-lang=\"csv\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003eid\u003c/span\u003e,\u003cspan style=\"color:#e6db74\"\u003elon\u003c/span\u003e,\u003cspan style=\"color:#e6db74\"\u003elat\u003c/span\u003e,\u003cspan style=\"color:#e6db74\"\u003edepth\u003c/span\u003e,\u003cspan style=\"color:#e6db74\"\u003eden\u003c/span\u003e,\u003cspan style=\"color:#e6db74\"\u003esus\u003c/span\u003e,\u003cspan style=\"color:#e6db74\"\u003ename\u003c/span\u003e,\u003cspan style=\"color:#e6db74\"\u003edate\u003c/span\u003e,\u003cspan style=\"color:#e6db74\"\u003elocation\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e,\u003cspan style=\"color:#e6db74\"\u003e32.23\u003c/span\u003e,\u003cspan style=\"color:#e6db74\"\u003e65.23\u003c/span\u003e,,\u003cspan style=\"color:#e6db74\"\u003e2.3\u003c/span\u003e,,\u003cspan style=\"color:#e6db74\"\u003egabbro\u003c/span\u003e,,\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;enshi, hubei\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e,\u003cspan style=\"color:#e6db74\"\u003e65.2\u003c/span\u003e,\u003cspan style=\"color:#e6db74\"\u003e12.7\u003c/span\u003e,,\u003cspan style=\"color:#e6db74\"\u003e2.5\u003c/span\u003e,,\u003cspan style=\"color:#e6db74\"\u003ebasalt\u003c/span\u003e,,\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;wuhan\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e,\u003cspan style=\"color:#e6db74\"\u003e70.23\u003c/span\u003e,\u003cspan style=\"color:#e6db74\"\u003e10.6\u003c/span\u003e,,\u003cspan style=\"color:#e6db74\"\u003e3.1\u003c/span\u003e,,\u003cspan style=\"color:#e6db74\"\u003evolcanic\u003c/span\u003e,,\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;hangzhou, zhejiang\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e加载并查看列信息：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-cpp\" data-lang=\"cpp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026#34;dsv_table.hpp\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    utilsxx\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003edsv_table t(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;data/sample_data\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;.csv\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    t.info(utilsxx\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eColInfo);  \u003cspan style=\"color:#75715e\"\u003e// 打印列信息\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e输出示例：\u003c/p\u003e","title":"dsv_table: 一文看懂 C++ 高性能表格数据处理"},{"content":"引言 几乎每个非平凡的程序都需要配置参数：输入文件路径、算法阈值、输出格式、模型参数……硬编码这些值会让程序失去灵活性，而手写解析逻辑又繁琐且容易出错。\ngetoption 是 utilsxx 库中的轻量级配置解析组件。它采用声明式设计理念：你先声明程序需要哪些参数、哪些是必填的、哪些参数属于互斥组，然后从文件或字符串向量中读取配置，getoption 会自动完成解析、校验和类型转换。本文将带你从基础用法到高阶技巧，彻底掌握这个工具。\n一、设计理念：声明式配置管理 传统的配置解析往往是\u0026quot;命令式\u0026quot;的：读一行、判断键名、手动转换类型、检查是否缺失。getoption 将其转变为\u0026quot;声明式\u0026quot;：\n// 1. 声明：我的程序需要这些参数 getoption gopt; gopt.add_options({\u0026#34;range\u0026#34;, \u0026#34;interval\u0026#34;, \u0026#34;output\u0026#34;}, {true, true, false}); // 2. 读取：从配置文件加载 gopt.read_options(\u0026#34;config.txt\u0026#34;); // 3. 校验：自动检查必填项和互斥组 gopt.check_mandatory(); // 4. 使用：直接获取已转换类型的值 auto range = gopt.get_value\u0026lt;std::string\u0026gt;(\u0026#34;range\u0026#34;); auto weight = gopt.get_values\u0026lt;double\u0026gt;(\u0026#34;weight\u0026#34;, \u0026#39;|\u0026#39;, \u0026#39;/\u0026#39;); 这种设计的优势：\n集中管理：所有参数定义在一处，一目了然 自动校验：必填项、互斥组自动检查，无需手写 if 判断 类型安全：模板函数自动完成字符串到目标类型的转换 别名支持：一个参数可以有多个名称（如 model-file/ModelFile/model_file） 二、快速上手 2.1 配置文件格式 getoption 使用简洁的 key-value 格式：\n# 以 # 开头的行是注释，会被自动跳过 range = 100/1000/200/2000/full_range interval = 10/10 weight = 120.12/232/114.2 model-file = test.txt # 空行也会被跳过，不影响解析 # 同一个 key 可以出现多次，值会自动合并 model-tag = model1 model-tag = model2 model-tag = model3 out-model = example.msh 格式规则：\n注释行：以 # 开头（可自定义） 空行：自动跳过 参数行：key \u0026lt;分隔符\u0026gt; value，默认分隔符为 = 多值合并：同一 key 多次出现，值用连接符拼接（默认逗号 ,） 2.2 基础代码示例 #include \u0026#34;get_option.hpp\u0026#34; #include \u0026lt;iostream\u0026gt; int main() try { // 步骤1：声明参数 utilsxx::getoption gopt; gopt.add_options( {\u0026#34;range\u0026#34;, \u0026#34;interval\u0026#34;, \u0026#34;weight\u0026#34;, \u0026#34;model-file\u0026#34;, \u0026#34;output\u0026#34;}, // 参数名 {true, true, false, true, false} // 是否必填 ); // 步骤2：读取配置文件 gopt.read_options(\u0026#34;config.txt\u0026#34;); // 步骤3：校验必填项 gopt.check_mandatory(); // 步骤4：获取参数值 std::string model = gopt.get_value\u0026lt;std::string\u0026gt;(\u0026#34;model-file\u0026#34;); std::vector\u0026lt;double\u0026gt; w = gopt.get_values\u0026lt;double\u0026gt;(\u0026#34;weight\u0026#34;, \u0026#39;|\u0026#39;, \u0026#39;/\u0026#39;); std::cout \u0026lt;\u0026lt; \u0026#34;Model: \u0026#34; \u0026lt;\u0026lt; model \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Weight count: \u0026#34; \u0026lt;\u0026lt; w.size() \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; return 0; } catch(utilsxx::error_handler \u0026amp;e) { e.show(); return 1; } 三、参数声明详解 3.1 逐个添加参数 gopt.add_option(\u0026#34;range\u0026#34;, true); // 必填参数 gopt.add_option(\u0026#34;output\u0026#34;, false); // 可选参数 gopt.add_option(\u0026#34;verbose\u0026#34;, false, 1); // 可选参数，属于组1 参数说明：\n参数 类型 说明 opt_name string 参数名称 manda bool 是否必填（true = 必填） gp int 所属组号（-1 = 不属于任何组） 3.2 批量添加参数 gopt.add_options( {\u0026#34;range\u0026#34;, \u0026#34;interval\u0026#34;, \u0026#34;weight\u0026#34;, \u0026#34;model-file\u0026#34;, \u0026#34;model-tag\u0026#34;, \u0026#34;out-model\u0026#34;, \u0026#34;save-model\u0026#34;}, {true, true, true, true, true, true, false} ); 两个 initializer_list 必须长度一致，否则会抛出异常。\n3.3 设置参数分组（互斥/至少选一） 分组用于实现\u0026quot;至少选一个\u0026quot;的校验逻辑：\n// out-model 和 save-model 属于组1，两者至少填一个 gopt.set_group(1, {\u0026#34;out-model\u0026#34;, \u0026#34;save-model\u0026#34;}); 校验时：\ngopt.check_group(1); // 检查组1：至少一个参数被设置 gopt.check_groups(); // 检查所有组 gopt.check_mandatory(); // 检查所有必填项（组内参数不检查） 注意：属于分组的参数在 check_mandatory() 中不会被检查，因为组校验已经保证了\u0026quot;至少一个\u0026quot;的存在性。\n四、读取配置 4.1 从文件读取 gopt.read_options( \u0026#34;data/option_sample\u0026#34;, // 文件名（自动追加 .txt） \u0026#34;=\u0026#34;, // key-value 分隔符（默认 =） \u0026#34;,\u0026#34;, // 多值连接符（默认 ,） \u0026#39;#\u0026#39; // 注释符（默认 #） ); 4.2 从字符串向量读取 适合从命令行参数或其他内存数据源读取：\nstd::vector\u0026lt;std::string\u0026gt; opts = { \u0026#34;# this is an annotation\u0026#34;, \u0026#34;range = 100/1000/200/2000/full_range\u0026#34;, \u0026#34;interval = 10/10\u0026#34;, \u0026#34;weight = 120.12/232/114.2\u0026#34;, \u0026#34;model-file = test.txt\u0026#34;, \u0026#34;\u0026#34;, // 空行会被跳过 \u0026#34;model-tag = model1\u0026#34;, \u0026#34;model-tag = model2\u0026#34;, \u0026#34;model-tag = model3\u0026#34;, \u0026#34;out-model = example.msh\u0026#34; }; gopt.read_options(opts); // 参数与文件版相同 4.3 多值合并行为 当同一个 key 在配置文件中多次出现时，值会自动合并：\nmodel-tag = model1 model-tag = model2 model-tag = model3 读取后 model-tag 的值为：model1,model2,model3（默认用逗号连接）\n你可以自定义连接符：\n// 用分号连接多值 gopt.read_options(\u0026#34;config.txt\u0026#34;, \u0026#34;=\u0026#34;, \u0026#34;;\u0026#34;); 五、获取参数值 5.1 获取单个值 // 获取字符串值 std::string model = gopt.get_value\u0026lt;std::string\u0026gt;(\u0026#34;model-file\u0026#34;); // 获取 double 值（自动类型转换） double val = gopt.get_value\u0026lt;double\u0026gt;(\u0026#34;threshold\u0026#34;); // 获取 int 值 int count = gopt.get_value\u0026lt;int\u0026gt;(\u0026#34;max-iterations\u0026#34;); // 获取 bool 值 bool verbose = gopt.get_value\u0026lt;bool\u0026gt;(\u0026#34;verbose\u0026#34;); 5.2 获取多值（向量） 配置文件中的值本身可以包含分隔符，用 get_values() 解析为向量：\nweight = 120.12/232/114.2 // 用 \u0026#39;/\u0026#39; 作为值内分隔符，解析为 double 向量 std::vector\u0026lt;double\u0026gt; weight = gopt.get_values\u0026lt;double\u0026gt;(\u0026#34;weight\u0026#34;, \u0026#39;|\u0026#39;, \u0026#39;/\u0026#39;); // 结果: {120.12, 232.0, 114.2} 参数说明：\n参数 说明 key 参数名（可含别名，如 \u0026quot;weight|Weight\u0026quot;） delimiter key 别名之间的分隔符（默认 |） sep 值内部的分隔符（默认空格） 5.3 参数别名 一个参数可以有多个名称，用 | 分隔：\n// 以下调用都会匹配同一个参数 auto w1 = gopt.get_value\u0026lt;std::string\u0026gt;(\u0026#34;model-file\u0026#34;); auto w2 = gopt.get_value\u0026lt;std::string\u0026gt;(\u0026#34;Model-File\u0026#34;); auto w3 = gopt.get_value\u0026lt;std::string\u0026gt;(\u0026#34;model_file\u0026#34;); // 更简洁的写法：一次声明多个别名 auto w = gopt.get_value\u0026lt;std::string\u0026gt;(\u0026#34;model-file|Model-File|model_file\u0026#34;); 这在处理不同命名风格的配置文件时非常有用（如用户可能写 model-file、ModelFile 或 model_file）。\n5.4 检查参数是否存在 if (gopt.has_value(\u0026#34;output\u0026#34;)) { std::string out = gopt.get_value\u0026lt;std::string\u0026gt;(\u0026#34;output\u0026#34;); } has_value() 同样支持别名：\nif (gopt.has_value(\u0026#34;output|Output|out\u0026#34;)) { // ... } 六、校验机制 6.1 必填项校验 gopt.add_options({\u0026#34;input\u0026#34;, \u0026#34;output\u0026#34;, \u0026#34;threshold\u0026#34;}, {true, true, false}); gopt.read_options(\u0026#34;config.txt\u0026#34;); gopt.check_mandatory(); // 如果 input 或 output 未设置，抛出异常 错误输出示例：\ngetoption: mandatory option not set: input getoption: mandatory option not set: output 6.2 分组校验（至少选一） gopt.add_options({\u0026#34;out-model\u0026#34;, \u0026#34;save-model\u0026#34;}, {false, false}); gopt.set_group(1, {\u0026#34;out-model\u0026#34;, \u0026#34;save-model\u0026#34;}); gopt.read_options(\u0026#34;config.txt\u0026#34;); gopt.check_group(1); // 两者至少填一个，否则抛出异常 错误输出示例：\ngetoption: need at least one of grouped options: out-model save-model 6.3 完整校验流程 gopt.read_options(\u0026#34;config.txt\u0026#34;); gopt.check_mandatory(); // 先检查必填项 gopt.check_groups(); // 再检查组约束 最佳实践：总是在 read_options() 之后立即调用校验函数，尽早发现配置错误。\n七、高级技巧 7.1 结合 str_utils 解析复杂值 getoption 与 str_utils.hpp 中的工具函数配合，可以解析更复杂的值格式：\n// 配置文件：range = 100/1000/200/2000/full_range std::string range_str = gopt.get_value\u0026lt;std::string\u0026gt;(\u0026#34;range\u0026#34;); // 用 parse_string_to_value 解析多个变量 double xmin, xmax, ymin, ymax; std::string cover_str; utilsxx::parse_string_to_value(range_str, \u0026#39;/\u0026#39;, true, xmin, xmax, ymin, ymax, cover_str); // 结果：xmin=100, xmax=1000, ymin=200, ymax=2000, cover_str=\u0026#34;full_range\u0026#34; std::cout \u0026lt;\u0026lt; xmin \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; xmax \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; ymin \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; ymax \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; cover_str \u0026lt;\u0026lt; std::endl; 7.2 显示所有参数 调试时查看所有已读取的参数：\ngopt.show_options(); // 只显示已设置的参数 gopt.show_options(std::clog, true); // 显示所有参数（包括未设置的） 输出示例：\nrange: 100/1000/200/2000/full_range interval: 10/10 weight: 120.12/232/114.2 model-file: test.txt model-tag: model1,model2,model3 out-model: example.msh ====================== 7.3 自定义分隔符 根据配置文件的格式灵活调整：\n// YAML 风格（冒号分隔） gopt.read_options(\u0026#34;config.yaml\u0026#34;, \u0026#34;:\u0026#34;); // INI 风格（等号分隔，分号注释） gopt.read_options(\u0026#34;config.ini\u0026#34;, \u0026#34;=\u0026#34;, \u0026#34;,\u0026#34;, \u0026#39;;\u0026#39;); // 空格分隔（极简格式） gopt.read_options(\u0026#34;config.txt\u0026#34;, \u0026#34; \u0026#34;); 7.4 多配置文件分层加载 实现类似\u0026quot;默认配置 + 用户配置\u0026quot;的覆盖机制：\n// 先加载默认配置 gopt.read_options(\u0026#34;default_config.txt\u0026#34;); // 再加载用户配置，同名参数会覆盖 gopt.read_options(\u0026#34;user_config.txt\u0026#34;); 因为 read_options() 对已存在的 key 会追加值（用连接符），要实现覆盖需要配合 clear()：\n// 方案：分开管理 gopt.read_options(\u0026#34;default_config.txt\u0026#34;); auto defaults = gopt; // 保存默认值 gopt.clear(); gopt.read_options(\u0026#34;user_config.txt\u0026#34;); // 然后手动合并... 7.5 与 CLI11 结合使用 utilsxx 同时集成了 CLI11 库用于命令行参数解析。两者可以互补：\n// 1. 用 CLI11 解析命令行参数 CLI::App app(\u0026#34;My Program\u0026#34;); std::string config_file; app.add_option(\u0026#34;-c,--config\u0026#34;, config_file, \u0026#34;Config file\u0026#34;); CLI11_PARSE(app, argc, argv); // 2. 用 getoption 解析配置文件 utilsxx::getoption gopt; gopt.add_options({...}, {...}); gopt.read_options(config_file); gopt.check_mandatory(); 八、完整示例 以下是一个科学计算程序的完整配置解析示例：\n#include \u0026#34;get_option.hpp\u0026#34; #include \u0026#34;vector.hpp\u0026#34; #include \u0026lt;iostream\u0026gt; #include \u0026lt;string\u0026gt; int main() try { utilsxx::getoption gopt; // 1. 声明所有参数 gopt.add_options({ \u0026#34;range\u0026#34;, // 计算范围 (必填) \u0026#34;interval\u0026#34;, // 采样间隔 (必填) \u0026#34;weight\u0026#34;, // 权重参数 (必填) \u0026#34;model-file\u0026#34;, // 模型文件 (必填) \u0026#34;model-tag\u0026#34;, // 模型标签 (必填，可多值) \u0026#34;out-model\u0026#34;, // 输出模型 \u0026#34;save-model\u0026#34; // 保存模型 }, { true, true, true, true, true, false, false }); // 2. 设置互斥组：out-model 和 save-model 至少选一个 gopt.set_group(1, {\u0026#34;out-model\u0026#34;, \u0026#34;save-model\u0026#34;}); // 3. 读取配置（可从文件或内存向量） gopt.read_options(\u0026#34;data/option_sample.txt\u0026#34;); // 4. 校验 gopt.check_mandatory(); gopt.check_group(1); // 5. 显示配置 gopt.show_options(); // 6. 获取并使用参数 std::string model_file = gopt.get_value\u0026lt;std::string\u0026gt;(\u0026#34;model-file\u0026#34;); std::cout \u0026lt;\u0026lt; \u0026#34;Model file: \u0026#34; \u0026lt;\u0026lt; model_file \u0026lt;\u0026lt; std::endl; // 获取多值权重 std::vector\u0026lt;double\u0026gt; weight = gopt.get_values\u0026lt;double\u0026gt;(\u0026#34;weight\u0026#34;, \u0026#39;|\u0026#39;, \u0026#39;/\u0026#39;); std::cout \u0026lt;\u0026lt; \u0026#34;Weights (\u0026#34; \u0026lt;\u0026lt; weight.size() \u0026lt;\u0026lt; \u0026#34;): \u0026#34;; for (auto w : weight) std::cout \u0026lt;\u0026lt; w \u0026lt;\u0026lt; \u0026#34; \u0026#34;; std::cout \u0026lt;\u0026lt; std::endl; // 获取多值标签 std::vector\u0026lt;std::string\u0026gt; tags = gopt.get_values\u0026lt;std::string\u0026gt;(\u0026#34;model-tag\u0026#34;, \u0026#39;|\u0026#39;, \u0026#39;,\u0026#39;); std::cout \u0026lt;\u0026lt; \u0026#34;Tags: \u0026#34; \u0026lt;\u0026lt; tags \u0026lt;\u0026lt; std::endl; // 解析范围参数 double xmin, xmax, ymin, ymax; std::string cover_str; utilsxx::parse_string_to_value( gopt.get_value\u0026lt;std::string\u0026gt;(\u0026#34;range|Range\u0026#34;), \u0026#39;/\u0026#39;, true, xmin, xmax, ymin, ymax, cover_str); std::cout \u0026lt;\u0026lt; \u0026#34;Range: [\u0026#34; \u0026lt;\u0026lt; xmin \u0026lt;\u0026lt; \u0026#34;, \u0026#34; \u0026lt;\u0026lt; xmax \u0026lt;\u0026lt; \u0026#34;] x [\u0026#34; \u0026lt;\u0026lt; ymin \u0026lt;\u0026lt; \u0026#34;, \u0026#34; \u0026lt;\u0026lt; ymax \u0026lt;\u0026lt; \u0026#34;], mode: \u0026#34; \u0026lt;\u0026lt; cover_str \u0026lt;\u0026lt; std::endl; return 0; } catch(utilsxx::error_handler \u0026amp;e) { e.show(); return 1; } 对应配置文件 option_sample.txt：\n# this is an annotation range = 100/1000/200/2000/full_range interval = 10/10 weight = 120.12/232/114.2 model-file = test.txt # you can insert empty lines too model-tag = model1 model-tag = model2 model-tag = model3 out-model = example.msh 九、API 速查表 参数声明 方法 说明 add_option(name, mandatory, group) 添加单个参数 add_options(names, mandatories) 批量添加参数 set_group(gp, names) 将参数归入互斥组 配置读取 方法 说明 read_options(filename, sep, conn, annotate) 从文件读取 read_options(str_vec, sep, conn, annotate) 从字符串向量读取 校验 方法 说明 check_mandatory() 检查必填项（组内参数除外） check_group(gp) 检查组内至少一个参数已设置 check_groups() 检查所有组 查询 方法 说明 has_value(key, delimiter) 检查参数是否已设置 get_value\u0026lt;T\u0026gt;(key, delimiter, sep) 获取单个值（自动类型转换） get_values\u0026lt;T\u0026gt;(key, delimiter, sep) 获取多值向量 show_options(out, show_all) 显示所有参数 clear() 清空所有参数 辅助函数 函数 说明 parse_key_value(str, sep, key, value) 解析 key-value 字符串 十、设计哲学与最佳实践 10.1 何时使用 getoption？ 场景 推荐方案 程序需要大量配置参数 getoption 参数有复杂的校验规则（必填、互斥） getoption 配置文件需要注释和空行 getoption 纯命令行工具，参数少 CLI11 需要子命令支持 CLI11 两者结合 CLI11 解析命令行 + getoption 解析配置文件 10.2 命名建议 使用 kebab-case（短横线连接）：model-file、max-iterations 同时注册 camelCase 别名：model-file|modelFile 避免使用下划线开头（可能与内部变量冲突） 10.3 错误处理 getoption 在以下情况会抛出 error_handler 异常：\n必填参数未设置（check_mandatory()） 组内参数全部缺失（check_group()） 读取了不存在的 key（get_value()） 初始化列表长度不匹配（add_options()） 建议始终用 try-catch 包裹：\ntry { // ... 配置解析代码 ... } catch(utilsxx::error_handler \u0026amp;e) { e.show(); // 打印友好的错误信息 return 1; } 结语 getoption 以声明式的设计简化了 C++ 程序的配置管理。你只需声明\u0026quot;程序需要什么参数\u0026quot;，剩下的解析、校验、类型转换都由它自动完成。配合别名支持、分组校验和多值解析，它能优雅地处理从简单脚本到复杂科学计算程序的各种配置需求。\n项目地址：utilsxx\n","permalink":"https://geowisdom.com.cn/posts/resource/utilsxx/readme_get_option/","summary":"\u003ch2 id=\"引言\"\u003e引言\u003c/h2\u003e\n\u003cp\u003e几乎每个非平凡的程序都需要配置参数：输入文件路径、算法阈值、输出格式、模型参数……硬编码这些值会让程序失去灵活性，而手写解析逻辑又繁琐且容易出错。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003egetoption\u003c/code\u003e 是 \u003ca href=\"https://github.com/geophyxx/utilsxx\"\u003eutilsxx\u003c/a\u003e 库中的轻量级配置解析组件。它采用\u003cstrong\u003e声明式\u003c/strong\u003e设计理念：你先声明程序需要哪些参数、哪些是必填的、哪些参数属于互斥组，然后从文件或字符串向量中读取配置，\u003ccode\u003egetoption\u003c/code\u003e 会自动完成解析、校验和类型转换。本文将带你从基础用法到高阶技巧，彻底掌握这个工具。\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"一设计理念声明式配置管理\"\u003e一、设计理念：声明式配置管理\u003c/h2\u003e\n\u003cp\u003e传统的配置解析往往是\u0026quot;命令式\u0026quot;的：读一行、判断键名、手动转换类型、检查是否缺失。\u003ccode\u003egetoption\u003c/code\u003e 将其转变为\u0026quot;声明式\u0026quot;：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-cpp\" data-lang=\"cpp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// 1. 声明：我的程序需要这些参数\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egetoption gopt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egopt.add_options({\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;range\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;interval\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;output\u0026#34;\u003c/span\u003e}, {true, true, false});\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// 2. 读取：从配置文件加载\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egopt.read_options(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;config.txt\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// 3. 校验：自动检查必填项和互斥组\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egopt.check_mandatory();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// 4. 使用：直接获取已转换类型的值\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eauto\u003c/span\u003e range \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e gopt.get_value\u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003estd\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003estring\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;range\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eauto\u003c/span\u003e weight \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e gopt.get_values\u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003edouble\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;weight\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;|\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;/\u0026#39;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e这种设计的优势：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e集中管理\u003c/strong\u003e：所有参数定义在一处，一目了然\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e自动校验\u003c/strong\u003e：必填项、互斥组自动检查，无需手写 \u003ccode\u003eif\u003c/code\u003e 判断\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e类型安全\u003c/strong\u003e：模板函数自动完成字符串到目标类型的转换\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e别名支持\u003c/strong\u003e：一个参数可以有多个名称（如 \u003ccode\u003emodel-file\u003c/code\u003e/\u003ccode\u003eModelFile\u003c/code\u003e/\u003ccode\u003emodel_file\u003c/code\u003e）\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003ch2 id=\"二快速上手\"\u003e二、快速上手\u003c/h2\u003e\n\u003ch3 id=\"21-配置文件格式\"\u003e2.1 配置文件格式\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003egetoption\u003c/code\u003e 使用简洁的 \u003cstrong\u003ekey-value\u003c/strong\u003e 格式：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-text\" data-lang=\"text\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e# 以 # 开头的行是注释，会被自动跳过\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003erange = 100/1000/200/2000/full_range\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003einterval = 10/10\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eweight = 120.12/232/114.2\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emodel-file = test.txt\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e# 空行也会被跳过，不影响解析\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e# 同一个 key 可以出现多次，值会自动合并\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emodel-tag = model1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emodel-tag = model2\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emodel-tag = model3\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eout-model = example.msh\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e格式规则：\u003c/p\u003e","title":"get_option: C++ 配置文件解析，一文从入门到精通"},{"content":"引言 科学计算程序往往需要运行数分钟甚至数小时。在这漫长的等待中，用户常常面临几个痛点：\n程序是否在正常运行？进度如何？ 发现参数设置有误，能否暂停调整而不是强制终止？ 想查看当前中间结果，又不想打断计算流程？ process_monitor 是 utilsxx 库中的交互式进程监控组件。它以非侵入式的设计理念，让你只需继承一个基类、重写几个虚函数，就能为任何计算任务赋予暂停/继续/状态查看/安全退出的交互能力。本文将带你彻底掌握它的设计与使用。\n一、核心设计：为什么需要 process_monitor？ 1.1 传统方案的局限 方案 问题 Ctrl+C 强制终止 丢失中间结果，无法优雅收尾 信号处理（SIGINT） 跨平台差异大，代码复杂 日志文件轮询 实时性差，无法交互控制 独立 GUI 监控 引入重量级依赖，不适合服务器环境 1.2 process_monitor 的设计哲学 非侵入式：你的计算逻辑几乎不用修改，只需在循环中插入一行 wait_for_keyboard() 跨平台：Windows（_kbhit/_getch）和 Unix（termios/select）统一封装 线程安全：键盘监控与计算逻辑分离，通过条件变量同步 可扩展：所有交互行为都是虚函数，可按需重写 1.3 状态机模型 RUNNING --[pause_key]--\u0026gt; PAUSED --[continue_key]--\u0026gt; RUNNING | | |--[quit_key] |--[quit_key] v v STOPPED \u0026lt;----------------- STOPPED 二、快速上手 2.1 最小示例 #include \u0026#34;process_monitor.h\u0026#34; class MyCalculator : public utilsxx::process_monitor { public: void process() override { while (!end_process()) { // 你的计算逻辑... do_calculation_step(); // 每秒钟检查一次键盘输入 wait_for_keyboard(1); } } }; int main() { MyCalculator calc; calc.start_monitoring(); return 0; } 运行后，你可以随时按下：\np — 暂停计算 c — 继续计算 s — 查看当前状态 q — 安全退出（带确认） 三、架构详解 3.1 双线程架构 ┌─────────────────┐ ┌─────────────────┐ │ 计算线程 │ │ 键盘监控线程 │ │ (process()) │◄────│ (keyboard_) │ │ │ cv │ │ │ wait_for_ │ │ get_keyboard_ │ │ keyboard() │ │ input() │ └─────────────────┘ └─────────────────┘ │ │ └────── 共享状态 ────────┘ end_process_, state_, iteration_count_ 计算线程：执行你的 process() 虚函数 键盘监控线程：以 100ms 间隔轮询键盘，解析命令 同步机制：std::mutex + std::condition_variable 实现暂停/唤醒 3.2 跨平台键盘输入 平台 技术 特点 Windows _kbhit() / _getch() 原生支持，无需额外设置 Linux/macOS termios + select() 关闭规范模式和回显，实现非阻塞读取 Unix 下的终端模式切换：\n// 进入非阻塞模式（键盘监控时） tio.c_lflag \u0026amp;= ~(ICANON | ECHO); // 关闭规范模式和回显 tio.c_cc[VMIN] = 0; // 最小字符数为0 tio.c_cc[VTIME] = 0; // 无超时 // 恢复规范模式（确认对话框时） tio.c_lflag |= (ICANON | ECHO); // 开启规范模式和回显 注意：析构函数会自动恢复终端模式，防止程序异常退出后终端行为异常。\n四、配置与定制 4.1 MonitorConfig 配置结构 struct MonitorConfig { char pause_key = \u0026#39;p\u0026#39;; // 暂停键 char quit_key = \u0026#39;q\u0026#39;; // 退出键 char status_key = \u0026#39;s\u0026#39;; // 状态键 char continue_key = \u0026#39;c\u0026#39;; // 继续键 bool show_status_on_pause = true; // 暂停时自动显示状态 int check_interval_ms = 100; // 键盘检查间隔（毫秒） }; 4.2 自定义按键 class MyCalculator : public utilsxx::process_monitor { public: MyCalculator() : process_monitor(MonitorConfig{ \u0026#39; \u0026#39;, // 空格键暂停 \u0026#39;q\u0026#39;, // q 键退出 \u0026#39;i\u0026#39;, // i 键显示信息 \u0026#39;r\u0026#39;, // r 键继续（resume） true, // 暂停时显示状态 50 // 50ms 检查间隔（更灵敏） }) {} void process() override { /* ... */ } }; 4.3 可重写的虚函数 虚函数 默认行为 重写场景 process() 纯虚函数，必须实现 你的核心计算逻辑 show_status() 显示状态、运行时间、迭代次数 显示自定义指标（如误差、收敛度） on_pause() 显示暂停提示 保存检查点、释放资源 on_resume() 显示恢复提示 重新加载检查点、恢复资源 五、完整示例：蒙特卡洛计算 π 以下是一个完整的交互式科学计算示例：\n#include \u0026#34;process_monitor.h\u0026#34; #include \u0026lt;cmath\u0026gt; #include \u0026lt;random\u0026gt; #include \u0026lt;iomanip\u0026gt; #include \u0026lt;sstream\u0026gt; using namespace utilsxx; class MonteCarloPiCalculator : public process_monitor { private: int total_points_; int points_in_circle_; double current_pi_estimate_; public: MonteCarloPiCalculator() : process_monitor(MonitorConfig{ \u0026#39; \u0026#39;, // 空格键暂停 \u0026#39;q\u0026#39;, // q键退出 \u0026#39;i\u0026#39;, // i键显示信息 \u0026#39;c\u0026#39;, // c键继续 true, // 暂停时显示状态 50 // 50ms检查间隔 }), total_points_(0), points_in_circle_(0), current_pi_estimate_(0.0) {} void process() override { std::random_device rd; std::mt19937 gen(rd()); std::uniform_real_distribution\u0026lt;\u0026gt; dis(-1.0, 1.0); std::cout \u0026lt;\u0026lt; \u0026#34;Starting Monte Carlo π calculation...\u0026#34; \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;Press SPACE to pause, \u0026#39;i\u0026#39; for info, \u0026#39;q\u0026#39; to quit.\u0026#34; \u0026lt;\u0026lt; std::endl; while (!end_process()) { // 生成随机点 double x = dis(gen); double y = dis(gen); // 检查点是否在单位圆内 if (x*x + y*y \u0026lt;= 1.0) { points_in_circle_++; } total_points_++; // 计算π的估计值 current_pi_estimate_ = 4.0 * points_in_circle_ / total_points_; // 每1000次迭代更新状态信息 if (total_points_ % 1000 == 0) { std::stringstream ss; ss \u0026lt;\u0026lt; \u0026#34;π ≈ \u0026#34; \u0026lt;\u0026lt; std::fixed \u0026lt;\u0026lt; std::setprecision(8) \u0026lt;\u0026lt; current_pi_estimate_ \u0026lt;\u0026lt; \u0026#34; (error: \u0026#34; \u0026lt;\u0026lt; std::abs(current_pi_estimate_ - M_PI) \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; set_status_info(ss.str()); } // 更新迭代计数 increment_iteration(); // 模拟计算延迟 std::this_thread::sleep_for(std::chrono::milliseconds(1)); } std::cout \u0026lt;\u0026lt; \u0026#34;\\nFinal result after \u0026#34; \u0026lt;\u0026lt; total_points_ \u0026lt;\u0026lt; \u0026#34; iterations:\u0026#34; \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;π ≈ \u0026#34; \u0026lt;\u0026lt; std::fixed \u0026lt;\u0026lt; std::setprecision(10) \u0026lt;\u0026lt; current_pi_estimate_ \u0026lt;\u0026lt; std::endl; } // 重写状态显示：展示计算细节 void show_status() override { process_monitor::show_status(); // 调用基类显示基本状态 std::cout \u0026lt;\u0026lt; \u0026#34;Calculation Details:\u0026#34; \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34; Total points: \u0026#34; \u0026lt;\u0026lt; total_points_ \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34; Points in circle: \u0026#34; \u0026lt;\u0026lt; points_in_circle_ \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34; Current π estimate: \u0026#34; \u0026lt;\u0026lt; std::fixed \u0026lt;\u0026lt; std::setprecision(8) \u0026lt;\u0026lt; current_pi_estimate_ \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34; Actual π value: \u0026#34; \u0026lt;\u0026lt; M_PI \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34; Accuracy: \u0026#34; \u0026lt;\u0026lt; std::fixed \u0026lt;\u0026lt; std::setprecision(2) \u0026lt;\u0026lt; (1.0 - std::abs(current_pi_estimate_ - M_PI) / M_PI) * 100.0 \u0026lt;\u0026lt; \u0026#34;%\u0026#34; \u0026lt;\u0026lt; std::endl; } // 重写暂停回调 void on_pause() override { process_monitor::on_pause(); std::cout \u0026lt;\u0026lt; \u0026#34;\\nCalculation paused at iteration \u0026#34; \u0026lt;\u0026lt; get_iteration_count() \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;Current π estimate: \u0026#34; \u0026lt;\u0026lt; current_pi_estimate_ \u0026lt;\u0026lt; std::endl; } // 重写恢复回调 void on_resume() override { process_monitor::on_resume(); std::cout \u0026lt;\u0026lt; \u0026#34;\\nResuming calculation from iteration \u0026#34; \u0026lt;\u0026lt; get_iteration_count() \u0026lt;\u0026lt; std::endl; } }; int main() { MonteCarloPiCalculator calculator; calculator.start_monitoring(); return 0; } 运行效果：\nStarting Monte Carlo π calculation... Press SPACE to pause, \u0026#39;i\u0026#39; for info, \u0026#39;q\u0026#39; to quit. === Process Status === State: RUNNING Runtime: 15 seconds Iterations: 14283 Info: π ≈ 3.14159245 (error: 1.927e-07) Commands: \u0026#39; \u0026#39;=pause, \u0026#39;c\u0026#39;=continue, \u0026#39;i\u0026#39;=status, \u0026#39;q\u0026#39;=quit ==================== Calculation Details: Total points: 14283000 Points in circle: 11227491 Current π estimate: 3.14159245 Actual π value: 3.14159265 Accuracy: 99.999994% 六、API 详解 6.1 进程控制 方法 说明 start_monitoring() 启动计算线程和键盘监控线程，阻塞直到计算结束 process() 纯虚函数，实现你的核心计算逻辑 wait_for_keyboard(sec) 在计算循环中调用，检查暂停/退出状态 end_process() 检查是否应该结束循环 set_end_process(ask) 设置结束标志，ask=true 时会要求确认 6.2 状态查询 方法 说明 get_state() 获取当前状态（RUNNING/PAUSED/STOPPED） get_runtime_seconds() 获取已运行时间（秒） get_iteration_count() 获取迭代次数 show_status() 显示当前状态（可重写） 6.3 回调钩子 方法 触发时机 on_pause() 按下暂停键后 on_resume() 按下继续键后 show_status() 按下状态键后 6.4 辅助方法 方法 说明 increment_iteration() 迭代计数加一 set_status_info(str) 设置状态信息字符串 七、高级技巧 7.1 在暂停时保存检查点 void on_pause() override { process_monitor::on_pause(); // 保存当前计算状态到文件 std::ofstream checkpoint(\u0026#34;checkpoint.dat\u0026#34;); checkpoint \u0026lt;\u0026lt; iteration \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; current_result \u0026lt;\u0026lt; std::endl; checkpoint.close(); std::cout \u0026lt;\u0026lt; \u0026#34;Checkpoint saved.\u0026#34; \u0026lt;\u0026lt; std::endl; } void on_resume() override { // 可选：从检查点恢复 process_monitor::on_resume(); } 7.2 动态调整计算参数 void process() override { while (!end_process()) { // 根据当前状态调整步长 if (get_state() == ProcessState::RUNNING) { step_size = adaptive_step_size(); } compute_step(step_size); increment_iteration(); wait_for_keyboard(1); } } 7.3 多阶段任务监控 void process() override { // 阶段1：预处理 std::cout \u0026lt;\u0026lt; \u0026#34;Phase 1: Preprocessing...\u0026#34; \u0026lt;\u0026lt; std::endl; for (int i = 0; i \u0026lt; 100 \u0026amp;\u0026amp; !end_process(); i++) { preprocess_step(i); increment_iteration(); wait_for_keyboard(1); } // 阶段2：主计算 std::cout \u0026lt;\u0026lt; \u0026#34;Phase 2: Main computation...\u0026#34; \u0026lt;\u0026lt; std::endl; set_status_info(\u0026#34;Entering main computation phase\u0026#34;); while (!end_process() \u0026amp;\u0026amp; !converged()) { compute_step(); increment_iteration(); wait_for_keyboard(1); } // 阶段3：后处理 std::cout \u0026lt;\u0026lt; \u0026#34;Phase 3: Post-processing...\u0026#34; \u0026lt;\u0026lt; std::endl; // ... } 7.4 与进度条结合 #include \u0026#34;process_monitor.h\u0026#34; #include \u0026#34;progress_bar.hpp\u0026#34; class MonitoredTask : public utilsxx::process_monitor { utilsxx::progress_bar bar_; public: void process() override { bar_.hide_cursor(); bar_.reset(\u0026#34;Computing\u0026#34;, 1000); for (int i = 0; i \u0026lt; 1000 \u0026amp;\u0026amp; !end_process(); i++) { bar_.tick(); do_work(); increment_iteration(); wait_for_keyboard(1); } bar_.show_cursor(); } }; 八、注意事项 8.1 线程安全 end_process()、get_state()、get_iteration_count() 使用 std::atomic，线程安全 set_status_info() 内部有锁保护，可在计算线程安全调用 不要在 process() 中直接操作终端输入输出（交给键盘线程处理） 8.2 终端模式恢复 如果程序异常崩溃导致终端模式未恢复，可以执行：\nreset # 或 stty sane 8.3 重定向输出时的行为 当程序输出被重定向到文件时，键盘交互仍然有效（因为监控使用 std::cin 而非 std::cout）：\n./my_program \u0026gt; results.log # 计算结果写入文件，键盘交互仍在终端 九、API 速查表 类与枚举 类型 说明 ProcessState RUNNING, PAUSED, STOPPED MonitorConfig 配置结构体（按键映射、检查间隔） process_monitor 监控基类 构造函数与生命周期 方法 说明 process_monitor(config) 构造函数，传入配置 ~process_monitor() 析构时自动回收线程、恢复终端 start_monitoring() 启动监控，阻塞直到结束 计算线程使用 方法 说明 process() 纯虚函数，实现计算逻辑 wait_for_keyboard(sec) 检查控制状态，支持暂停唤醒 end_process() 判断是否应结束循环 increment_iteration() 迭代计数加一 set_status_info(str) 设置状态信息 状态查询 方法 说明 get_state() 获取当前运行状态 get_runtime_seconds() 获取运行时间 get_iteration_count() 获取迭代次数 可重写回调 方法 说明 show_status() 显示状态信息 on_pause() 暂停时调用 on_resume() 恢复时调用 结语 process_monitor 为 C++ 科学计算程序提供了一种轻量级、非侵入式、跨平台的交互监控方案。它不需要 GUI、不需要网络、不需要复杂配置——只需继承基类，你的程序就拥有了暂停、继续、状态查看和安全退出的能力。\n对于长时间运行的数值模拟、数据处理和优化算法，这种\u0026quot;随时可控\u0026quot;的体验不仅是便利，更是生产力的保障。\n项目地址：utilsxx\n","permalink":"https://geowisdom.com.cn/posts/resource/utilsxx/readme_process_monitor/","summary":"\u003ch2 id=\"引言\"\u003e引言\u003c/h2\u003e\n\u003cp\u003e科学计算程序往往需要运行数分钟甚至数小时。在这漫长的等待中，用户常常面临几个痛点：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e程序是否在正常运行？进度如何？\u003c/li\u003e\n\u003cli\u003e发现参数设置有误，能否暂停调整而不是强制终止？\u003c/li\u003e\n\u003cli\u003e想查看当前中间结果，又不想打断计算流程？\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003ccode\u003eprocess_monitor\u003c/code\u003e 是 \u003ca href=\"https://github.com/geophyxx/utilsxx\"\u003eutilsxx\u003c/a\u003e 库中的交互式进程监控组件。它以\u003cstrong\u003e非侵入式\u003c/strong\u003e的设计理念，让你只需继承一个基类、重写几个虚函数，就能为任何计算任务赋予\u003cstrong\u003e暂停/继续/状态查看/安全退出\u003c/strong\u003e的交互能力。本文将带你彻底掌握它的设计与使用。\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"一核心设计为什么需要-process_monitor\"\u003e一、核心设计：为什么需要 process_monitor？\u003c/h2\u003e\n\u003ch3 id=\"11-传统方案的局限\"\u003e1.1 传统方案的局限\u003c/h3\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e方案\u003c/th\u003e\n          \u003cth\u003e问题\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eCtrl+C\u003c/code\u003e 强制终止\u003c/td\u003e\n          \u003ctd\u003e丢失中间结果，无法优雅收尾\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e信号处理（\u003ccode\u003eSIGINT\u003c/code\u003e）\u003c/td\u003e\n          \u003ctd\u003e跨平台差异大，代码复杂\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e日志文件轮询\u003c/td\u003e\n          \u003ctd\u003e实时性差，无法交互控制\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e独立 GUI 监控\u003c/td\u003e\n          \u003ctd\u003e引入重量级依赖，不适合服务器环境\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"12-process_monitor-的设计哲学\"\u003e1.2 process_monitor 的设计哲学\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e非侵入式\u003c/strong\u003e：你的计算逻辑几乎不用修改，只需在循环中插入一行 \u003ccode\u003ewait_for_keyboard()\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e跨平台\u003c/strong\u003e：Windows（\u003ccode\u003e_kbhit\u003c/code\u003e/\u003ccode\u003e_getch\u003c/code\u003e）和 Unix（\u003ccode\u003etermios\u003c/code\u003e/\u003ccode\u003eselect\u003c/code\u003e）统一封装\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e线程安全\u003c/strong\u003e：键盘监控与计算逻辑分离，通过条件变量同步\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e可扩展\u003c/strong\u003e：所有交互行为都是虚函数，可按需重写\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"13-状态机模型\"\u003e1.3 状态机模型\u003c/h3\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eRUNNING --[pause_key]--\u0026gt; PAUSED --[continue_key]--\u0026gt; RUNNING\n   |                        |\n   |--[quit_key]            |--[quit_key]\n   v                        v\nSTOPPED \u0026lt;----------------- STOPPED\n\u003c/code\u003e\u003c/pre\u003e\u003chr\u003e\n\u003ch2 id=\"二快速上手\"\u003e二、快速上手\u003c/h2\u003e\n\u003ch3 id=\"21-最小示例\"\u003e2.1 最小示例\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-cpp\" data-lang=\"cpp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026#34;process_monitor.h\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eMyCalculator\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e utilsxx\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eprocess_monitor {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e process() \u003cspan style=\"color:#66d9ef\"\u003eoverride\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e!\u003c/span\u003eend_process()) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#75715e\"\u003e// 你的计算逻辑...\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            do_calculation_step();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#75715e\"\u003e// 每秒钟检查一次键盘输入\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            wait_for_keyboard(\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e};\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    MyCalculator calc;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    calc.start_monitoring();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e运行后，你可以随时按下：\u003c/p\u003e","title":"process_monitor: C++ 交互式进程监控，让科学计算可控可停"},{"content":"引言 命令行程序在处理耗时任务时，一个直观的进度条能极大提升用户体验。然而 C++ 标准库并未提供终端进度条组件，开发者要么引入重型第三方库，要么自己手写 ANSI 转义码控制光标。\nprogress_bar 是 utilsxx 库中的轻量级终端进度条组件，它零依赖、跨平台、开箱即用，同时提供了 indicators 第三方库的集成示例，满足不同场景需求。本文将带你一文看懂如何使用它，以及有哪些实用技巧。\n一、快速上手：三行代码显示进度条 #include \u0026#34;progress_bar.hpp\u0026#34; int main() { utilsxx::progress_bar bar(\u0026#34;Processing\u0026#34;, 101); for (int i = 0; i \u0026lt; 101; ++i) { bar.tick(); // 每步调用一次 // 你的耗时操作... } return 0; } 终端输出效果：\nProcessing |████████████████████████████████████████░░░░░░░░░░| 75.0% 二、核心设计 2.1 两种显示模式 模式 枚举值 说明 完整模式 Full 进度条 + 百分比数字（默认） 数字模式 NumberOnly 仅显示百分比数字，无图形条 2.2 颜色系统 支持已完成部分和未完成部分分别设置颜色：\n颜色 枚举值 ANSI 效果 白色 White 默认粗体 绿色 Green 粗体绿色 红色 Red 粗体红色 蓝色 Blue 粗体蓝色 黄色 Yellow 粗体黄色 2.3 智能宽度适配 进度条长度自动根据终端宽度计算，无需手动调整：\nbar_length = (terminal_width - description_width - 5) / 2 这意味着无论终端窗口大小如何变化，进度条都能自动适配，不会溢出或留白过多。\n三、基础用法详解 3.1 构造函数与 reset() // 方式1：构造时初始化 utilsxx::progress_bar bar(\u0026#34;Task-1\u0026#34;, 101, utilsxx::Full, std::clog); // 方式2：默认构造后 reset utilsxx::progress_bar bar; bar.reset(\u0026#34;Task-2\u0026#34;, 201, utilsxx::Full, std::clog); 参数说明：\n参数 类型 默认值 说明 description string \u0026quot;\u0026quot; 进度条名称前缀 n unsigned long 101 总步数（循环次数） mode progbar_mode_e Full 显示模式 out ostream\u0026amp; std::clog 输出流，默认标准错误输出 为何默认输出到 std::clog？ 避免进度条干扰程序的标准输出重定向（如 program \u0026gt; result.txt），进度信息仍显示在终端。\n3.2 自定义样式 bar.set_style( \u0026#34;\u0026gt;\u0026#34;, // 已完成符号 \u0026#34;-\u0026#34;, // 未完成符号 utilsxx::Green, // 已完成颜色 utilsxx::White // 未完成颜色 ); 效果：\nSample-2 |\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;----------------------| 50.0% 3.3 完整示例 #include \u0026#34;progress_bar.hpp\u0026#34; #include \u0026lt;thread\u0026gt; #include \u0026lt;chrono\u0026gt; int main() { utilsxx::progress_bar bar; bar.hide_cursor(); // 隐藏光标，避免闪烁 // 第一段任务：默认样式 for (int i = 0; i \u0026lt; 101; ++i) { bar.tick(); std::this_thread::sleep_for(std::chrono::milliseconds(10)); } // 第二段任务：自定义样式 bar.reset(\u0026#34;Sample-2\u0026#34;, 201); bar.set_style(\u0026#34;\u0026gt;\u0026#34;, \u0026#34;-\u0026#34;, utilsxx::Green, utilsxx::White); for (int i = 0; i \u0026lt; 201; ++i) { bar.tick(); std::this_thread::sleep_for(std::chrono::milliseconds(10)); } bar.show_cursor(); // 恢复光标显示 return 0; } 四、高级技巧 4.1 处理未知总步数的任务：动态三点进度条 有些任务（如下载、网络请求）无法预知总步数，此时可用 dots() 方法显示动态三点动画：\nbar.reset(\u0026#34;Downloading data from Internet\u0026#34;); for (int i = 0; i \u0026lt; 20; ++i) { bar.dots(); // 显示 \u0026#34;Downloading...\u0026#34; -\u0026gt; \u0026#34;Downloading..\u0026#34; -\u0026gt; \u0026#34;Downloading.\u0026#34; 循环 std::this_thread::sleep_for(std::chrono::milliseconds(500)); } bar.done(); // 输出 \u0026#34;Downloading data from Internet... done!\u0026#34; dots() 的内部机制：\n固定每 500 毫秒 更新一次显示，不受调用频率影响 点的数量在 1-3 之间循环，用空格清除残留字符 适合配合 execute_with_progress() 用于异步任务 4.2 异步任务封装：execute_with_progress() 对于真正的异步任务（如文件下载、数据库查询），使用 execute_with_progress() 模板函数：\nbar.reset(\u0026#34;Downloading data from Internet\u0026#34;); utilsxx::execute_with_progress(bar, []() { // 你的耗时任务... std::this_thread::sleep_for(std::chrono::seconds(5)); }); 原理：\n自动隐藏光标 在工作线程中执行你的函数 主线程循环调用 dots() 显示进度动画 任务完成后自动调用 done() 并恢复光标 可自定义检查间隔（默认 250ms）：\n// 对于长时间任务，减少刷新频率以降低开销 utilsxx::execute_with_progress(bar, long_task, 1000); // 每秒检查一次 4.3 光标控制 bar.hide_cursor(); // 隐藏光标，进度条更新时不会闪烁 // ... 任务执行中 ... bar.show_cursor(); // 恢复光标显示 注意：hide_cursor() 和 show_cursor() 使用 ANSI 转义码 \\033[?25l / \\033[?25h，在析构函数中会自动恢复光标，防止程序异常退出后光标消失。\n4.4 多阶段任务管理 利用 reset() 可以在同一个进度条对象上执行多阶段任务：\nutilsxx::progress_bar bar; bar.hide_cursor(); // 阶段1：数据加载 bar.reset(\u0026#34;Loading data\u0026#34;, 100); for (int i = 0; i \u0026lt; 100; ++i) { bar.tick(); /* load */ } // 阶段2：数据处理 bar.reset(\u0026#34;Processing\u0026#34;, 500); bar.set_style(\u0026#34;#\u0026#34;, \u0026#34; \u0026#34;); for (int i = 0; i \u0026lt; 500; ++i) { bar.tick(); /* process */ } // 阶段3：结果保存 bar.reset(\u0026#34;Saving results\u0026#34;, 50); for (int i = 0; i \u0026lt; 50; ++i) { bar.tick(); /* save */ } bar.show_cursor(); 五、indicators 库：更丰富的进度条生态 utilsxx 同时集成了 indicators 库（位于 lib/indicators.hpp），提供更丰富的进度条样式：\n5.1 Unicode 进度条 #include \u0026#34;indicators.hpp\u0026#34; indicators::ProgressBar bar{ indicators::option::BarWidth{50}, indicators::option::Start{\u0026#34;[\u0026#34;}, indicators::option::Fill{\u0026#34;🔥\u0026#34;}, indicators::option::Lead{\u0026#34;🔥\u0026#34;}, indicators::option::Remainder{\u0026#34; \u0026#34;}, indicators::option::End{\u0026#34; ]\u0026#34;}, indicators::option::PostfixText{\u0026#34;Emojis\u0026#34;}, indicators::option::ForegroundColor{indicators::Color::white}, indicators::option::FontStyles{ std::vector\u0026lt;indicators::FontStyle\u0026gt;{indicators::FontStyle::bold}} }; while (true) { bar.tick(); if (bar.is_completed()) break; } 5.2 块进度条 indicators::BlockProgressBar bar{ indicators::option::BarWidth{80}, indicators::option::FontStyles{ std::vector\u0026lt;indicators::FontStyle\u0026gt;{indicators::FontStyle::bold}} }; size_t progress = 0; while (true) { bar.set_progress(progress++); if (bar.is_completed()) break; } 5.3 选择建议 场景 推荐方案 简单任务，快速集成 utilsxx::progress_bar 需要 Unicode/Emoji 支持 indicators::ProgressBar 需要不确定进度动画 utilsxx::progress_bar::dots() 需要多进度条并发 indicators 的多进度条支持 极简依赖，单头文件 utilsxx::progress_bar 六、API 速查表 progress_bar 类 方法 说明 progress_bar(desc, n, mode, out) 构造函数 reset(desc, n, mode, out) 重置进度条状态 tick() 前进一步，更新显示 dots() 显示动态三点动画 done() 输出 \u0026ldquo;\u0026hellip; done!\u0026rdquo; set_style(bar, space, done_color, todo_color) 自定义符号和颜色 hide_cursor() 隐藏终端光标 show_cursor() 显示终端光标 辅助函数 函数 说明 execute_with_progress(bar, func, interval) 异步执行函数并显示三点进度 枚举类型 枚举 值 progbar_mode_e Full, NumberOnly progbar_color_e White, Green, Red, Blue, Yellow 七、完整示例 #include \u0026#34;progress_bar.hpp\u0026#34; #include \u0026lt;thread\u0026gt; #include \u0026lt;chrono\u0026gt; #include \u0026lt;vector\u0026gt; // 模拟一个多阶段数据处理任务 void process_dataset() { utilsxx::progress_bar bar; bar.hide_cursor(); // 阶段1：读取文件（100个文件） bar.reset(\u0026#34;Reading files\u0026#34;, 100); for (int i = 0; i \u0026lt; 100; ++i) { bar.tick(); std::this_thread::sleep_for(std::chrono::milliseconds(5)); } // 阶段2：计算（500步，绿色进度条） bar.reset(\u0026#34;Computing\u0026#34;, 500); bar.set_style(\u0026#34;\u0026gt;\u0026#34;, \u0026#34;-\u0026#34;, utilsxx::Green, utilsxx::White); for (int i = 0; i \u0026lt; 500; ++i) { bar.tick(); std::this_thread::sleep_for(std::chrono::milliseconds(2)); } // 阶段3：保存结果（未知步数，使用异步进度） bar.reset(\u0026#34;Saving to database\u0026#34;); utilsxx::execute_with_progress(bar, []() { std::this_thread::sleep_for(std::chrono::seconds(3)); }, 500); bar.show_cursor(); } int main() { process_dataset(); return 0; } 结语 progress_bar 以极简的 API 提供了完整的终端进度条功能：自动宽度适配、颜色控制、多阶段复用、异步任务支持。对于需要更花哨效果的项目，utilsxx 也集成了 indicators 库作为补充。\n无论是科学计算、数据处理还是文件转换，给耗时操作加上一个进度条，都是提升用户体验最简单有效的方式。\n项目地址：utilsxx\n","permalink":"https://geowisdom.com.cn/posts/resource/utilsxx/readme_progress_bar/","summary":"\u003ch2 id=\"引言\"\u003e引言\u003c/h2\u003e\n\u003cp\u003e命令行程序在处理耗时任务时，一个直观的进度条能极大提升用户体验。然而 C++ 标准库并未提供终端进度条组件，开发者要么引入重型第三方库，要么自己手写 ANSI 转义码控制光标。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eprogress_bar\u003c/code\u003e 是 \u003ca href=\"https://github.com/geophyxx/utilsxx\"\u003eutilsxx\u003c/a\u003e 库中的轻量级终端进度条组件，它\u003cstrong\u003e零依赖、跨平台、开箱即用\u003c/strong\u003e，同时提供了 \u003ccode\u003eindicators\u003c/code\u003e 第三方库的集成示例，满足不同场景需求。本文将带你一文看懂如何使用它，以及有哪些实用技巧。\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"一快速上手三行代码显示进度条\"\u003e一、快速上手：三行代码显示进度条\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-cpp\" data-lang=\"cpp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026#34;progress_bar.hpp\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    utilsxx\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eprogress_bar bar(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Processing\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e101\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e i \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e; i \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e101\u003c/span\u003e; \u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003ei) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        bar.tick();  \u003cspan style=\"color:#75715e\"\u003e// 每步调用一次\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e// 你的耗时操作...\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e终端输出效果：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eProcessing |████████████████████████████████████████░░░░░░░░░░|  75.0%\n\u003c/code\u003e\u003c/pre\u003e\u003chr\u003e\n\u003ch2 id=\"二核心设计\"\u003e二、核心设计\u003c/h2\u003e\n\u003ch3 id=\"21-两种显示模式\"\u003e2.1 两种显示模式\u003c/h3\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e模式\u003c/th\u003e\n          \u003cth\u003e枚举值\u003c/th\u003e\n          \u003cth\u003e说明\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003e完整模式\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eFull\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e进度条 + 百分比数字（默认）\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003e数字模式\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eNumberOnly\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e仅显示百分比数字，无图形条\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"22-颜色系统\"\u003e2.2 颜色系统\u003c/h3\u003e\n\u003cp\u003e支持已完成部分和未完成部分分别设置颜色：\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e颜色\u003c/th\u003e\n          \u003cth\u003e枚举值\u003c/th\u003e\n          \u003cth\u003eANSI 效果\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e白色\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eWhite\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e默认粗体\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e绿色\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eGreen\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e粗体绿色\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e红色\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eRed\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e粗体红色\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e蓝色\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eBlue\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e粗体蓝色\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e黄色\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eYellow\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e粗体黄色\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"23-智能宽度适配\"\u003e2.3 智能宽度适配\u003c/h3\u003e\n\u003cp\u003e进度条长度\u003cstrong\u003e自动根据终端宽度计算\u003c/strong\u003e，无需手动调整：\u003c/p\u003e","title":"progress_bar: C++ 终端进度条，一文从入门到高阶"},{"content":"引言 在程序配置领域，TOML（Tom\u0026rsquo;s Obvious, Minimal Language）凭借其直观的语法和强类型特性，逐渐成为 JSON 和 YAML 的有力替代者。与 JSON 相比，TOML 支持注释、更易于手写；与 YAML 相比，TOML 语法更严格、歧义更少。\ntoml11 是一个现代 C++17 的 TOML 解析库，以header-only、类型安全、异常友好著称。utilsxx 将其作为外部组件集成到 lib/toml.hpp 中，方便项目直接使用。本文将基于 toml11 的完整功能，结合 utilsxx 中的示例，带你彻底掌握 TOML 在 C++ 中的读写技巧。\n一、TOML 速览 1.1 为什么选择 TOML？ 特性 TOML JSON YAML 注释支持 ✅ ❌ ✅ 强类型 ✅ ✅ ❌（隐式转换多） 手写友好 ✅ ⚠️ ⚠️（缩进敏感） 日期时间 ✅ 原生 ❌ ✅ 表（Table） ✅ ❌（用对象模拟） ✅ 歧义性 低 低 高 1.2 TOML 核心语法 # 这是注释 # 键值对（基本类型） title = \u0026#34;TOML Example\u0026#34; enabled = true port = 8080 pi = 3.14159 # 表（类似 JSON 的对象） [owner] name = \u0026#34;Tom Preston-Werner\u0026#34; dob = 1979-05-27T07:32:00-08:00 # 嵌套表 [servers.alpha] ip = \u0026#34;10.0.0.1\u0026#34; role = \u0026#34;frontend\u0026#34; # 数组 ports = [8000, 8001, 8002] # 表数组（数组的每个元素是一个表） [[products]] name = \u0026#34;Hammer\u0026#34; sku = 738594937 [[products]] name = \u0026#34;Nail\u0026#34; sku = 284758393 二、快速上手 2.1 解析 TOML 文件 #include \u0026#34;toml.hpp\u0026#34; #include \u0026lt;iostream\u0026gt; int main() { // 解析文件 const auto root = toml::parse(\u0026#34;config.toml\u0026#34;); // 获取值 std::string title = toml::find\u0026lt;std::string\u0026gt;(root, \u0026#34;title\u0026#34;); std::cout \u0026lt;\u0026lt; \u0026#34;Title: \u0026#34; \u0026lt;\u0026lt; title \u0026lt;\u0026lt; std::endl; return 0; } 2.2 两种访问方式 toml11 提供两种访问风格：\n方式一：成员函数链式访问\nstd::string name = root.at(\u0026#34;owner\u0026#34;).at(\u0026#34;name\u0026#34;).as_string(); bool enabled = root.at(\u0026#34;database\u0026#34;).at(\u0026#34;enabled\u0026#34;).as_boolean(); int port0 = root.at(\u0026#34;database\u0026#34;).at(\u0026#34;ports\u0026#34;).at(0).as_integer(); 方式二：toml::find 模板函数（推荐）\nstd::string name = toml::find\u0026lt;std::string\u0026gt;(root, \u0026#34;owner\u0026#34;, \u0026#34;name\u0026#34;); bool enabled = toml::find\u0026lt;bool\u0026gt;(root, \u0026#34;database\u0026#34;, \u0026#34;enabled\u0026#34;); std::vector\u0026lt;int\u0026gt; ports = toml::find\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;(root, \u0026#34;database\u0026#34;, \u0026#34;ports\u0026#34;); toml::find 的优势：\n类型安全：编译期确定返回类型 自动转换：可直接转换为 std::vector、std::map、std::pair 等 STL 容器 异常信息友好：键不存在时抛出清晰的异常 三、基础类型访问 3.1 标量类型 # config.toml name = \u0026#34;Alice\u0026#34; age = 30 salary = 5000.50 active = true const auto root = toml::parse(\u0026#34;config.toml\u0026#34;); // 字符串 std::string name = toml::find\u0026lt;std::string\u0026gt;(root, \u0026#34;name\u0026#34;); // 整数 int age = toml::find\u0026lt;int\u0026gt;(root, \u0026#34;age\u0026#34;); // 浮点数 double salary = toml::find\u0026lt;double\u0026gt;(root, \u0026#34;salary\u0026#34;); // 布尔值 bool active = toml::find\u0026lt;bool\u0026gt;(root, \u0026#34;active\u0026#34;); 3.2 成员函数对照表 当使用 at() 链式访问时，需要用对应的 as_*() 方法转换：\nTOML 类型 成员函数 C++ 类型 String as_string() std::string Integer as_integer() std::int64_t Float as_floating() double Boolean as_boolean() bool Datetime as_offset_datetime() toml::offset_datetime Array is_array() / at(i) toml::value Table is_table() / at(key) toml::value 四、容器类型访问 4.1 数组（Array） # array_example.toml integers = [1, 2, 3] colors = [\u0026#34;red\u0026#34;, \u0026#34;yellow\u0026#34;, \u0026#34;green\u0026#34;] nested_arrays_of_ints = [[1, 2], [3, 4, 5]] const auto root = toml::parse(\u0026#34;data/array_example.toml\u0026#34;); // 方式1：逐个访问 int first = root.at(\u0026#34;integers\u0026#34;).at(0).as_integer(); // 1 std::string color = root.at(\u0026#34;colors\u0026#34;).at(1).as_string(); // \u0026#34;yellow\u0026#34; // 方式2：整体转换为 vector std::vector\u0026lt;int\u0026gt; integers = toml::find\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;(root, \u0026#34;integers\u0026#34;); std::vector\u0026lt;std::string\u0026gt; colors = toml::find\u0026lt;std::vector\u0026lt;std::string\u0026gt;\u0026gt;(root, \u0026#34;colors\u0026#34;); // 方式3：转换为固定大小数组 std::array\u0026lt;std::string, 3\u0026gt; color_arr = toml::find\u0026lt;std::array\u0026lt;std::string, 3\u0026gt;\u0026gt;(root, \u0026#34;colors\u0026#34;); // 嵌套数组 std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; nested = toml::find\u0026lt;std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026gt;(root, \u0026#34;nested_arrays_of_ints\u0026#34;); 4.2 表（Table） # config.toml [database] enabled = true ports = [8000, 8001, 8002] [database.temp_targets] cpu = 79.5 case = 72.0 const auto root = toml::parse(\u0026#34;config.toml\u0026#34;); // 访问嵌套表 bool enabled = toml::find\u0026lt;bool\u0026gt;(root, \u0026#34;database\u0026#34;, \u0026#34;enabled\u0026#34;); // 将表转换为 map std::map\u0026lt;std::string, double\u0026gt; targets = toml::find\u0026lt;std::map\u0026lt;std::string, double\u0026gt;\u0026gt;(root, \u0026#34;database\u0026#34;, \u0026#34;temp_targets\u0026#34;); // targets[\u0026#34;cpu\u0026#34;] == 79.5 // 将整个 database 表作为子树访问 const auto\u0026amp; db = toml::find(root, \u0026#34;database\u0026#34;); std::vector\u0026lt;int\u0026gt; ports = toml::find\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;(db, \u0026#34;ports\u0026#34;); 4.3 混合类型数组 TOML 允许数组元素类型不同：\nnumbers = [0.1, 0.2, 0.5, 1, 2, 5] // 转换为 tuple（前3个 double，后3个 int） auto numbers = toml::find\u0026lt;std::tuple\u0026lt;double, double, double, int, int, int\u0026gt;\u0026gt;(root, \u0026#34;numbers\u0026#34;); double f1 = std::get\u0026lt;0\u0026gt;(numbers); // 0.1 int i1 = std::get\u0026lt;3\u0026gt;(numbers); // 1 4.4 表数组（Array of Tables） 这是 TOML 最强大的特性之一，适合表示列表数据：\n[[products]] name = \u0026#34;Hammer\u0026#34; sku = 738594937 [[products]] name = \u0026#34;Nail\u0026#34; sku = 284758393 color = \u0026#34;gray\u0026#34; const auto root = toml::parse(\u0026#34;data/array_of_tables_example.toml\u0026#34;); // 方式1：逐个访问 std::string name0 = root.at(\u0026#34;products\u0026#34;).at(0).at(\u0026#34;name\u0026#34;).as_string(); // \u0026#34;Hammer\u0026#34; int sku1 = root.at(\u0026#34;products\u0026#34;).at(1).at(\u0026#34;sku\u0026#34;).as_integer(); // 284758393 // 方式2：转换为 vector + map std::vector\u0026lt;std::map\u0026lt;std::string, int\u0026gt;\u0026gt; products = toml::find\u0026lt;std::vector\u0026lt;std::map\u0026lt;std::string, int\u0026gt;\u0026gt;\u0026gt;(root, \u0026#34;points\u0026#34;); // points[0][\u0026#34;x\u0026#34;] == 1 // 方式3：自定义结构体（推荐） struct product_t { product_t(const toml::value\u0026amp; v) : name(toml::find_or\u0026lt;std::string\u0026gt;(v, \u0026#34;name\u0026#34;, \u0026#34;\u0026#34;)), sku(toml::find_or\u0026lt;std::uint64_t\u0026gt;(v, \u0026#34;sku\u0026#34;, 0)), color(toml::find_or\u0026lt;std::string\u0026gt;(v, \u0026#34;color\u0026#34;, \u0026#34;\u0026#34;)) {} std::string name; std::uint64_t sku; std::string color; }; std::vector\u0026lt;product_t\u0026gt; products = toml::find\u0026lt;std::vector\u0026lt;product_t\u0026gt;\u0026gt;(root, \u0026#34;products\u0026#34;); 五、高级访问技巧 5.1 安全访问：find_or 当键可能不存在时，使用 find_or 提供默认值：\n// 如果 \u0026#34;timeout\u0026#34; 不存在，返回 30 int timeout = toml::find_or(root, \u0026#34;timeout\u0026#34;, 30); // 如果 \u0026#34;user.email\u0026#34; 不存在，返回空字符串 std::string email = toml::find_or(root, \u0026#34;user\u0026#34;, \u0026#34;email\u0026#34;, std::string(\u0026#34;\u0026#34;)); 5.2 检查键是否存在 if (root.contains(\u0026#34;optional_key\u0026#34;)) { auto val = toml::find\u0026lt;std::string\u0026gt;(root, \u0026#34;optional_key\u0026#34;); } // 检查类型 if (root.at(\u0026#34;maybe_string\u0026#34;).is_string()) { // ... } 5.3 获取注释 toml11 会保留文件顶部的注释：\n# This is a TOML document. # This contains most of the examples in the spec. [keys] key = \u0026#34;value\u0026#34; const auto root = toml::parse(\u0026#34;data/key_example.toml\u0026#34;); assert(root.comments().size() == 2); assert(root.comments().at(0) == \u0026#34; This is a TOML document.\u0026#34;); 5.4 点键访问（Dotted Keys） TOML 支持用点号表示嵌套：\nfruits.apple.skin = \u0026#34;thin\u0026#34; fruits.apple.color = \u0026#34;red\u0026#34; std::string skin = toml::find\u0026lt;std::string\u0026gt;(root, \u0026#34;fruits\u0026#34;, \u0026#34;apple\u0026#34;, \u0026#34;skin\u0026#34;); // \u0026#34;thin\u0026#34; std::string color = toml::find\u0026lt;std::string\u0026gt;(root, \u0026#34;fruits\u0026#34;, \u0026#34;apple\u0026#34;, \u0026#34;color\u0026#34;); // \u0026#34;red\u0026#34; 5.5 引号键与特殊键名 TOML 允许用引号定义包含特殊字符的键：\n\u0026#34;127.0.0.1\u0026#34; = \u0026#34;value\u0026#34; \u0026#34;character encoding\u0026#34; = \u0026#34;value\u0026#34; \u0026#34;\u0026#34; = \u0026#34;blank\u0026#34; std::string ip = toml::find\u0026lt;std::string\u0026gt;(root, \u0026#34;keys\u0026#34;, \u0026#34;127.0.0.1\u0026#34;); std::string empty_key = toml::find\u0026lt;std::string\u0026gt;(root, \u0026#34;keys\u0026#34;, \u0026#34;\u0026#34;); 六、日期时间类型 TOML 原生支持多种日期时间格式：\n[owner] dob = 1979-05-27T07:32:00-08:00 const auto dob = toml::find\u0026lt;toml::offset_datetime\u0026gt;(root, \u0026#34;owner\u0026#34;, \u0026#34;dob\u0026#34;); // 访问各个字段 assert(dob.date.year == 1979); assert(dob.date.month == static_cast\u0026lt;int\u0026gt;(toml::month_t::May)); assert(dob.date.day == 27); assert(dob.time.hour == 7); assert(dob.time.minute == 32); assert(dob.time.second == 0); assert(dob.offset.hour == -8); // 时区偏移 日期时间类型对照：\nTOML 类型 C++ 类型 示例 Local Date toml::local_date 1979-05-27 Local Time toml::local_time 07:32:00 Local Datetime toml::local_datetime 1979-05-27T07:32:00 Offset Datetime toml::offset_datetime 1979-05-27T07:32:00-08:00 七、自定义类型转换 toml11 的强大之处在于可以直接将 TOML 数据转换为自定义结构体：\n7.1 从构造函数转换 struct contributor_t { contributor_t(const toml::value\u0026amp; v) { if (v.is_string()) { name = v.as_string(); } else { name = toml::find\u0026lt;std::string\u0026gt;(v, \u0026#34;name\u0026#34;); email = toml::find_or(v, \u0026#34;email\u0026#34;, std::string(\u0026#34;\u0026#34;)); url = toml::find_or(v, \u0026#34;url\u0026#34;, std::string(\u0026#34;\u0026#34;)); } } std::string name; std::string email; std::string url; }; // 数组中混合了字符串和表，都能正确解析 std::vector\u0026lt;contributor_t\u0026gt; contributors = toml::find\u0026lt;std::vector\u0026lt;contributor_t\u0026gt;\u0026gt;(root, \u0026#34;contributors\u0026#34;); 7.2 复杂嵌套结构 struct fruit_t { fruit_t(const toml::value\u0026amp; v) : name(toml::find\u0026lt;std::string\u0026gt;(v, \u0026#34;name\u0026#34;)), physical(toml::find\u0026lt;std::map\u0026lt;std::string, std::string\u0026gt;\u0026gt;(v, \u0026#34;physical\u0026#34;)), varieties(toml::find\u0026lt;std::vector\u0026lt;std::map\u0026lt;std::string, std::string\u0026gt;\u0026gt;\u0026gt;(v, \u0026#34;varieties\u0026#34;)) {} std::string name; std::map\u0026lt;std::string, std::string\u0026gt; physical; std::vector\u0026lt;std::map\u0026lt;std::string, std::string\u0026gt;\u0026gt; varieties; }; std::vector\u0026lt;fruit_t\u0026gt; fruits = toml::find\u0026lt;std::vector\u0026lt;fruit_t\u0026gt;\u0026gt;(root, \u0026#34;fruits\u0026#34;); 八、生成 TOML 除了解析，toml11 也支持生成 TOML 文件：\n#include \u0026#34;toml.hpp\u0026#34; int main() { // 构建 TOML 值 toml::value root; root[\u0026#34;title\u0026#34;] = \u0026#34;My Application\u0026#34;; root[\u0026#34;version\u0026#34;] = \u0026#34;1.0.0\u0026#34;; // 创建表 toml::value database; database[\u0026#34;host\u0026#34;] = \u0026#34;localhost\u0026#34;; database[\u0026#34;port\u0026#34;] = 5432; database[\u0026#34;enabled\u0026#34;] = true; root[\u0026#34;database\u0026#34;] = database; // 创建数组 root[\u0026#34;features\u0026#34;] = toml::array{\u0026#34;auth\u0026#34;, \u0026#34;logging\u0026#34;, \u0026#34;metrics\u0026#34;}; // 序列化为字符串 std::string toml_str = toml::format(root); std::cout \u0026lt;\u0026lt; toml_str \u0026lt;\u0026lt; std::endl; // 保存到文件 std::ofstream file(\u0026#34;output.toml\u0026#34;); file \u0026lt;\u0026lt; toml_str; return 0; } 输出：\ntitle = \u0026#34;My Application\u0026#34; version = \u0026#34;1.0.0\u0026#34; [database] host = \u0026#34;localhost\u0026#34; port = 5432 enabled = true features = [\u0026#34;auth\u0026#34;, \u0026#34;logging\u0026#34;, \u0026#34;metrics\u0026#34;] 九、错误处理 toml11 使用异常报告错误，建议始终用 try-catch 包裹：\ntry { const auto root = toml::parse(\u0026#34;config.toml\u0026#34;); auto val = toml::find\u0026lt;int\u0026gt;(root, \u0026#34;nonexistent_key\u0026#34;); } catch (const toml::syntax_error\u0026amp; e) { // TOML 语法错误（如缺少引号、括号不匹配） std::cerr \u0026lt;\u0026lt; \u0026#34;Syntax error: \u0026#34; \u0026lt;\u0026lt; e.what() \u0026lt;\u0026lt; std::endl; } catch (const toml::type_error\u0026amp; e) { // 类型不匹配（如尝试将字符串 as_integer()） std::cerr \u0026lt;\u0026lt; \u0026#34;Type error: \u0026#34; \u0026lt;\u0026lt; e.what() \u0026lt;\u0026lt; std::endl; } catch (const toml::out_of_range\u0026amp; e) { // 键不存在或数组越界 std::cerr \u0026lt;\u0026lt; \u0026#34;Key not found: \u0026#34; \u0026lt;\u0026lt; e.what() \u0026lt;\u0026lt; std::endl; } 十、完整示例：程序配置管理 以下是一个科学计算程序的完整 TOML 配置解析示例：\n# simulation.toml title = \u0026#34;Magnetic Inversion\u0026#34; author = \u0026#34;Research Team\u0026#34; [model] file = \u0026#34;model.mesh\u0026#34; tags = [\u0026#34;sedimentary\u0026#34;, \u0026#34;igneous\u0026#34;, \u0026#34;metamorphic\u0026#34;] [computation] range = [100.0, 1000.0, 200.0, 2000.0] interval = [10, 10] max_iterations = 5000 convergence_threshold = 1e-6 [output] save_model = true out_file = \u0026#34;result.vtk\u0026#34; [[sensors]] name = \u0026#34;Sensor-A\u0026#34; position = [100.5, 200.3, 50.0] active = true [[sensors]] name = \u0026#34;Sensor-B\u0026#34; position = [150.2, 180.7, 45.0] active = false #include \u0026#34;toml.hpp\u0026#34; #include \u0026lt;iostream\u0026gt; #include \u0026lt;vector\u0026gt; struct sensor_t { sensor_t(const toml::value\u0026amp; v) : name(toml::find\u0026lt;std::string\u0026gt;(v, \u0026#34;name\u0026#34;)), position(toml::find\u0026lt;std::vector\u0026lt;double\u0026gt;\u0026gt;(v, \u0026#34;position\u0026#34;)), active(toml::find_or(v, \u0026#34;active\u0026#34;, true)) {} std::string name; std::vector\u0026lt;double\u0026gt; position; bool active; }; int main() try { const auto root = toml::parse(\u0026#34;simulation.toml\u0026#34;); // 基本信息 std::string title = toml::find\u0026lt;std::string\u0026gt;(root, \u0026#34;title\u0026#34;); std::cout \u0026lt;\u0026lt; \u0026#34;Simulation: \u0026#34; \u0026lt;\u0026lt; title \u0026lt;\u0026lt; std::endl; // 模型配置 std::string model_file = toml::find\u0026lt;std::string\u0026gt;(root, \u0026#34;model\u0026#34;, \u0026#34;file\u0026#34;); std::vector\u0026lt;std::string\u0026gt; tags = toml::find\u0026lt;std::vector\u0026lt;std::string\u0026gt;\u0026gt;(root, \u0026#34;model\u0026#34;, \u0026#34;tags\u0026#34;); // 计算参数 std::vector\u0026lt;double\u0026gt; range = toml::find\u0026lt;std::vector\u0026lt;double\u0026gt;\u0026gt;(root, \u0026#34;computation\u0026#34;, \u0026#34;range\u0026#34;); int max_iter = toml::find\u0026lt;int\u0026gt;(root, \u0026#34;computation\u0026#34;, \u0026#34;max_iterations\u0026#34;); double threshold = toml::find\u0026lt;double\u0026gt;(root, \u0026#34;computation\u0026#34;, \u0026#34;convergence_threshold\u0026#34;); // 输出配置 bool save = toml::find\u0026lt;bool\u0026gt;(root, \u0026#34;output\u0026#34;, \u0026#34;save_model\u0026#34;); std::string out = toml::find_or(root, \u0026#34;output\u0026#34;, \u0026#34;out_file\u0026#34;, std::string(\u0026#34;default.out\u0026#34;)); // 传感器列表 std::vector\u0026lt;sensor_t\u0026gt; sensors = toml::find\u0026lt;std::vector\u0026lt;sensor_t\u0026gt;\u0026gt;(root, \u0026#34;sensors\u0026#34;); std::cout \u0026lt;\u0026lt; \u0026#34;Sensors: \u0026#34; \u0026lt;\u0026lt; sensors.size() \u0026lt;\u0026lt; std::endl; for (const auto\u0026amp; s : sensors) { std::cout \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; s.name \u0026lt;\u0026lt; \u0026#34; at (\u0026#34; \u0026lt;\u0026lt; s.position[0] \u0026lt;\u0026lt; \u0026#34;, \u0026#34; \u0026lt;\u0026lt; s.position[1] \u0026lt;\u0026lt; \u0026#34;, \u0026#34; \u0026lt;\u0026lt; s.position[2] \u0026lt;\u0026lt; \u0026#34;)\u0026#34; \u0026lt;\u0026lt; (s.active ? \u0026#34; [active]\u0026#34; : \u0026#34; [inactive]\u0026#34;) \u0026lt;\u0026lt; std::endl; } return 0; } catch (const std::exception\u0026amp; e) { std::cerr \u0026lt;\u0026lt; \u0026#34;Error: \u0026#34; \u0026lt;\u0026lt; e.what() \u0026lt;\u0026lt; std::endl; return 1; } 十一、API 速查表 解析 函数 说明 toml::parse(filename) 解析 TOML 文件 toml::parse(str) 从字符串解析 toml::parse(std::istream) 从输入流解析 查询 函数 说明 toml::find\u0026lt;T\u0026gt;(value, keys...) 按路径查找并转换为类型 T toml::find_or(value, key, default) 安全查找，不存在返回默认值 value.at(key) 成员函数访问 value.at(index) 数组索引访问 value.contains(key) 检查键是否存在 value.is_string() 等 类型检查 value.as_string() 等 类型转换 value.comments() 获取注释 生成 函数 说明 toml::format(value) 将值格式化为 TOML 字符串 toml::value 通用值类型 toml::array{...} 创建数组 结语 toml11 将 TOML 的简洁语法与 C++ 的类型安全完美结合。通过 toml::find，你可以用声明式的方式直接从 TOML 中提取类型正确的数据；通过自定义结构体的构造函数，复杂嵌套配置也能一键映射。对于需要手写配置文件的科学计算和工程应用，TOML + toml11 是一个值得长期投入的技术栈。\n项目地址：utilsxx 上游项目：toruniina/toml11\n","permalink":"https://geowisdom.com.cn/posts/resource/utilsxx/readme_toml11/","summary":"\u003ch2 id=\"引言\"\u003e引言\u003c/h2\u003e\n\u003cp\u003e在程序配置领域，TOML（Tom\u0026rsquo;s Obvious, Minimal Language）凭借其直观的语法和强类型特性，逐渐成为 JSON 和 YAML 的有力替代者。与 JSON 相比，TOML 支持注释、更易于手写；与 YAML 相比，TOML 语法更严格、歧义更少。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/ToruNiina/toml11\"\u003etoml11\u003c/a\u003e 是一个现代 C++17 的 TOML 解析库，以\u003cstrong\u003eheader-only\u003c/strong\u003e、\u003cstrong\u003e类型安全\u003c/strong\u003e、\u003cstrong\u003e异常友好\u003c/strong\u003e著称。utilsxx 将其作为外部组件集成到 \u003ca href=\"lib/toml.hpp\"\u003e\u003ccode\u003elib/toml.hpp\u003c/code\u003e\u003c/a\u003e 中，方便项目直接使用。本文将基于 toml11 的完整功能，结合 utilsxx 中的示例，带你彻底掌握 TOML 在 C++ 中的读写技巧。\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"一toml-速览\"\u003e一、TOML 速览\u003c/h2\u003e\n\u003ch3 id=\"11-为什么选择-toml\"\u003e1.1 为什么选择 TOML？\u003c/h3\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e特性\u003c/th\u003e\n          \u003cth\u003eTOML\u003c/th\u003e\n          \u003cth\u003eJSON\u003c/th\u003e\n          \u003cth\u003eYAML\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e注释支持\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e❌\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e强类型\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e❌（隐式转换多）\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e手写友好\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e⚠️\u003c/td\u003e\n          \u003ctd\u003e⚠️（缩进敏感）\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e日期时间\u003c/td\u003e\n          \u003ctd\u003e✅ 原生\u003c/td\u003e\n          \u003ctd\u003e❌\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e表（Table）\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e❌（用对象模拟）\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e歧义性\u003c/td\u003e\n          \u003ctd\u003e低\u003c/td\u003e\n          \u003ctd\u003e低\u003c/td\u003e\n          \u003ctd\u003e高\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"12-toml-核心语法\"\u003e1.2 TOML 核心语法\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-toml\" data-lang=\"toml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 这是注释\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 键值对（基本类型）\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003etitle\u003c/span\u003e = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;TOML Example\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eenabled\u003c/span\u003e = \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eport\u003c/span\u003e = \u003cspan style=\"color:#ae81ff\"\u003e8080\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003epi\u003c/span\u003e = \u003cspan style=\"color:#ae81ff\"\u003e3.14159\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 表（类似 JSON 的对象）\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#a6e22e\"\u003eowner\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ename\u003c/span\u003e = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Tom Preston-Werner\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003edob\u003c/span\u003e = \u003cspan style=\"color:#e6db74\"\u003e1979-05-27T07:32:00-08:00\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 嵌套表\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#a6e22e\"\u003eservers\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ealpha\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eip\u003c/span\u003e = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;10.0.0.1\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003erole\u003c/span\u003e = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;frontend\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 数组\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eports\u003c/span\u003e = [\u003cspan style=\"color:#ae81ff\"\u003e8000\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e8001\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e8002\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 表数组（数组的每个元素是一个表）\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[[\u003cspan style=\"color:#a6e22e\"\u003eproducts\u003c/span\u003e]]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ename\u003c/span\u003e = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Hammer\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003esku\u003c/span\u003e = \u003cspan style=\"color:#ae81ff\"\u003e738594937\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[[\u003cspan style=\"color:#a6e22e\"\u003eproducts\u003c/span\u003e]]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ename\u003c/span\u003e = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Nail\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003esku\u003c/span\u003e = \u003cspan style=\"color:#ae81ff\"\u003e284758393\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003chr\u003e\n\u003ch2 id=\"二快速上手\"\u003e二、快速上手\u003c/h2\u003e\n\u003ch3 id=\"21-解析-toml-文件\"\u003e2.1 解析 TOML 文件\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-cpp\" data-lang=\"cpp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026#34;toml.hpp\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026lt;iostream\u0026gt;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// 解析文件\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eauto\u003c/span\u003e root \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e toml\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eparse(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;config.toml\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// 获取值\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003estring title \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e toml\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003efind\u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003estd\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003estring\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e(root, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;title\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003ecout \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Title: \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e title \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eendl;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"22-两种访问方式\"\u003e2.2 两种访问方式\u003c/h3\u003e\n\u003cp\u003etoml11 提供两种访问风格：\u003c/p\u003e","title":"toml11: C++ TOML 解析完全指南"},{"content":"引言 在科学计算和工程应用领域，C++ 开发者常常面临一个尴尬的局面：标准库提供了基础的数据结构和算法，但面对具体的科研需求——比如读取一个带注释的 CSV 文件、在终端显示一个进度条、解析复杂的配置文件、或者监控一个耗时数小时的计算进程——往往需要自行编写大量\u0026quot;胶水代码\u0026quot;。\nUtilsXX 正是为解决这些痛点而生。它是一个基于 C++17 的轻量级工具库，专为地球科学、物理学和工程计算领域设计。不同于 Boost 这样的\u0026quot;全能型\u0026quot;库，UtilsXX 聚焦于科研工作者日常最高频的需求：数据处理、文件读写、终端交互、配置管理、时间计算和物理常数。本文将全面介绍 UtilsXX 的设计理念、核心模块和使用方法，帮助你快速上手并融入自己的项目。\n一、设计理念：为什么创建 UtilsXX？ 1.1 科研编程的特殊需求 科学计算程序与传统软件工程有显著不同：\n数据驱动：大量时间花在读取、清洗、转换各种格式的数据上 长时运行：数值模拟可能持续数小时甚至数天，需要进度反馈和交互控制 参数密集：算法通常有数十个可调参数，需要灵活的配置管理 跨平台协作：代码需要在个人笔记本、工作站和超算集群上无缝运行 快速迭代：研究思路变化快，代码需要易于修改和扩展 1.2 UtilsXX 的设计原则 原则 实践 Header-Only 优先 大部分组件为单头文件，直接 #include 即可使用 零依赖或内嵌依赖 核心模块无外部依赖；第三方库（如 nlohmann/json、toml11）直接内嵌 类型安全 大量使用模板和 static_assert，编译期捕获类型错误 异常友好 统一的 error_handler 错误处理机制，提供清晰的错误信息 跨平台 Windows、Linux、macOS 统一封装，终端操作透明适配 科研导向 内置地球物理常数、WGS84 参数、角度弧度转换等地学常用工具 1.3 与现有生态的关系 UtilsXX 不是要取代谁，而是填补\u0026quot;标准库不够用、重型框架太重\u0026quot;之间的空白：\nvs 标准库：补充了 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 构建，支持作为子项目或直接安装到系统：\n# 方式1：快速编译 mkdir build \u0026amp;\u0026amp; 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) 可选依赖：\nUTILSXX_USE_ARMADILLO=ON — 启用 Armadillo 线性代数封装 UTILSXX_USE_EIGEN3=ON — 启用 Eigen 矩阵库封装 2.3 第一个程序 #include \u0026#34;utilsxx/lib/vector.hpp\u0026#34; #include \u0026#34;utilsxx/lib/vector_operators.hpp\u0026#34; #include \u0026#34;utilsxx/lib/constants.hpp\u0026#34; #include \u0026lt;iostream\u0026gt; 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 \u0026lt;\u0026lt; \u0026#34;c = \u0026#34; \u0026lt;\u0026lt; c \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;Earth circumference ≈ \u0026#34; \u0026lt;\u0026lt; circumference / 1000 \u0026lt;\u0026lt; \u0026#34; km\u0026#34; \u0026lt;\u0026lt; std::endl; return 0; } 三、核心模块详解 3.1 向量与矩阵（vector.hpp / matrix.hpp） UtilsXX 提供了科研中最常用的数据结构的类型别名和算法：\n类型别名系统：\n// 一维向量 utilsxx::vector1d data; // std::vector\u0026lt;double\u0026gt; utilsxx::vector1i indices; // std::vector\u0026lt;int\u0026gt; utilsxx::vector1s labels; // std::vector\u0026lt;std::string\u0026gt; utilsxx::vector1cd complex; // std::vector\u0026lt;std::complex\u0026lt;double\u0026gt;\u0026gt; // 二维向量 utilsxx::vector2d grid; // std::vector\u0026lt;std::vector\u0026lt;double\u0026gt;\u0026gt; // 矩阵（内存连续的二维数组） utilsxx::matrix2d mat(100, 100, 0.0); // 100x100 零矩阵 向量算法：\nutilsxx::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\u0026lt;double\u0026gt;(v, [](double x) { return x * x; }); utilsxx::for_each(v, [](double\u0026amp; 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 技巧） 矩阵操作：\nutilsxx::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） 字符串与数值的相互转换是数据处理的日常：\n// 类型转字符串 std::string s = utilsxx::type2str(3.14159); // \u0026#34;3.14159\u0026#34; // 字符串转类型（支持自定义分隔符） double d; utilsxx::str2type(\u0026#34;3.14\u0026#34;, d); // 标准转换 utilsxx::str2type(\u0026#34;3/14\u0026#34;, d, \u0026#39;/\u0026#39;); // 分隔符替换后转换 // 特殊 double 转换（支持 Fortran 格式） double nan = utilsxx::str2double(\u0026#34;NAN\u0026#34;); // NaN double inf = utilsxx::str2double(\u0026#34;INF\u0026#34;); // Inf double sci = utilsxx::str2double(\u0026#34;1.5D+10\u0026#34;); // Fortran 科学计数法 // 字符串解析为向量 std::vector\u0026lt;double\u0026gt; vals; utilsxx::parse_string_to_vector(\u0026#34;1.0 2.0 3.0\u0026#34;, \u0026#39; \u0026#39;, vals); utilsxx::parse_string_to_vector(\u0026#34;1.0,2.0,3.0\u0026#34;, \u0026#39;,\u0026#39;, vals); // CSV 行解析（支持引号包裹字段） std::vector\u0026lt;std::string\u0026gt; fields; utilsxx::parse_csv_line(\u0026#34;name,\\\u0026#34;enshi, hubei\\\u0026#34;,age\u0026#34;, fields); // 结果: [\u0026#34;name\u0026#34;, \u0026#34;enshi, hubei\u0026#34;, \u0026#34;age\u0026#34;] // 多值解析 std::vector\u0026lt;std::string\u0026gt; tokens; utilsxx::parse_string_with_quotes(\u0026#34;cmd \\\u0026#34;arg with spaces\\\u0026#34; flag\u0026#34;, tokens); // 字符串替换 std::string result; utilsxx::replace_all(result, \u0026#34;hello world\u0026#34;, \u0026#34;world\u0026#34;, \u0026#34;UtilsXX\u0026#34;); // 字符串拼接（避免重复后缀） std::string path = utilsxx::patch_string(\u0026#34;data/file\u0026#34;, \u0026#34;.txt\u0026#34;); // \u0026#34;data/file.txt\u0026#34; std::string same = utilsxx::patch_string(\u0026#34;data/file.txt\u0026#34;, \u0026#34;.txt\u0026#34;); // \u0026#34;data/file.txt\u0026#34; 3.3 表格数据处理（dsv_table.hpp） dsv_table 是 UtilsXX 中最强大的数据处理组件之一，支持 CSV、TSV 和任意分隔符格式：\nutilsxx::dsv_table table; // 读取 CSV（自动识别列头） table.load_csv(\u0026#34;data/sample_data\u0026#34;); // 读取自定义分隔符文件 table.delimeter(\u0026#39;|\u0026#39;); table.head_number(1); table.load_text(\u0026#34;data/world_data\u0026#34;, \u0026#34;.txt\u0026#34;, utilsxx::ColHead | utilsxx::RowHead); // 数据访问（1-based，cell 为 0-based） std::string name = table.cell\u0026lt;std::string\u0026gt;(1, 0); // 第1行行名 std::vector\u0026lt;double\u0026gt; lon = table.get_column\u0026lt;double\u0026gt;(\u0026#34;lon\u0026#34;); std::vector\u0026lt;double\u0026gt; row2 = table.get_row\u0026lt;double\u0026gt;(2); // 过滤与排序 table.filter(\u0026#34;America\u0026#34;, \u0026#34;Continent_s\u0026#34;, utilsxx::ColHead); // 正则过滤 table.reorder\u0026lt;int\u0026gt;(\u0026#34;SurfaceArea_n\u0026#34;, utilsxx::ASCENDING); // 排序 // 输出控制（软删除） table.column_output(\u0026#34;deprecated\u0026#34;, utilsxx::Disable); // 导出为 JSON table.save_json(\u0026#34;output\u0026#34;, 0); // 对象数组格式 更多细节请参阅专门的 dsv_table 教程。\n3.4 配置参数解析（get_option.hpp） 声明式配置管理，让参数解析变得简洁：\nutilsxx::getoption gopt; // 声明参数 gopt.add_options( {\u0026#34;range\u0026#34;, \u0026#34;interval\u0026#34;, \u0026#34;weight\u0026#34;, \u0026#34;model-file\u0026#34;, \u0026#34;output\u0026#34;}, {true, true, false, true, false} // 是否必填 ); // 设置互斥组（至少选一个） gopt.set_group(1, {\u0026#34;out-model\u0026#34;, \u0026#34;save-model\u0026#34;}); // 读取配置 gopt.read_options(\u0026#34;config.txt\u0026#34;); // 自动校验 gopt.check_mandatory(); gopt.check_groups(); // 获取值（自动类型转换） std::string model = gopt.get_value\u0026lt;std::string\u0026gt;(\u0026#34;model-file\u0026#34;); std::vector\u0026lt;double\u0026gt; w = gopt.get_values\u0026lt;double\u0026gt;(\u0026#34;weight\u0026#34;, \u0026#39;|\u0026#39;, \u0026#39;/\u0026#39;); // 别名支持 auto v = gopt.get_value\u0026lt;std::string\u0026gt;(\u0026#34;model-file|ModelFile|model_file\u0026#34;); 3.5 终端交互（progress_bar.hpp / process_monitor.h / term_utils.hpp） 进度条：\nutilsxx::progress_bar bar(\u0026#34;Processing\u0026#34;, 1000); for (int i = 0; i \u0026lt; 1000; ++i) { bar.tick(); // 你的计算... } 进程监控（支持暂停/继续/状态查看）：\nclass 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(); // 按 \u0026#39;p\u0026#39; 暂停，\u0026#39;c\u0026#39; 继续，\u0026#39;s\u0026#39; 查看状态，\u0026#39;q\u0026#39; 退出 终端控制：\nint w = utilsxx::terminal_width(); // 获取终端宽度 int h = utilsxx::terminal_height(); // 获取终端高度 std::cout \u0026lt;\u0026lt; TERM_WIN_BOLDRED \u0026lt;\u0026lt; \u0026#34;Error!\u0026#34; \u0026lt;\u0026lt; TERM_WIN_RESET \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; TERM_WIN_CLEARALL; // 清屏 TERM_WIN_MOVETO(std::cout, 10, 20); // 移动光标到第10行第20列 3.6 时间处理（utc_time.hpp） 专为地球科学设计的时间结构体：\n// 构造时间 utilsxx::UTC_TIME t1(2024, 5, 10, 14, 30, 0, 0); utilsxx::UTC_TIME t2(\u0026#34;2024-05-10T14:30:00.000\u0026#34;); 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(); // \u0026#34;2024-5-10T14:30:0.000\u0026#34; std::string sym = t1.time_str(true); // \u0026#34;May. 10 2024 14:30:0.000\u0026#34; std::string rdseed = t1.rdseed_time_str(); // \u0026#34;2024.131.14.30.00.0000\u0026#34; 3.7 物理常数（constants.hpp） 内置地球科学常用常数：\n// 数学常数 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 内嵌了多个优秀的第三方库，方便统一使用：\nJSON 解析（nlohmann/json）：\n#include \u0026#34;json.hpp\u0026#34; nlohmann::json j = nlohmann::json::parse(\u0026#34;{\\\u0026#34;name\\\u0026#34;: \\\u0026#34;test\\\u0026#34;}\u0026#34;); std::string name = j[\u0026#34;name\u0026#34;]; TOML 解析（toml11）：\n#include \u0026#34;toml.hpp\u0026#34; auto root = toml::parse(\u0026#34;config.toml\u0026#34;); std::string title = toml::find\u0026lt;std::string\u0026gt;(root, \u0026#34;title\u0026#34;); 命令行解析（CLI11）：\n#include \u0026#34;CLI11.hpp\u0026#34; CLI::App app(\u0026#34;My Program\u0026#34;); std::string config; app.add_option(\u0026#34;-c,--config\u0026#34;, config, \u0026#34;Config file\u0026#34;); CLI11_PARSE(app, argc, argv); 高级进度条（indicators）：\n#include \u0026#34;indicators.hpp\u0026#34; indicators::ProgressBar bar{ indicators::option::BarWidth{50}, indicators::option::Fill{\u0026#34;🔥\u0026#34;} }; 四、错误处理机制 UtilsXX 使用统一的异常体系：\ntry { utilsxx::dsv_table table; table.load_csv(\u0026#34;nonexistent.csv\u0026#34;); } catch (const utilsxx::error_handler\u0026amp; e) { e.show(); // 打印格式化的错误信息 } error_handler 包含：\n错误代码（INVALID_PARA、RUNTIME_ERROR、IO_ERROR 等） 发生错误的函数名（__PRETTY_FUNCTION__） 详细的错误描述 五、完整项目示例 以下是一个典型的地球物理数据处理流程，展示了多个 UtilsXX 模块的协同使用：\n#include \u0026#34;utilsxx/lib/get_option.hpp\u0026#34; #include \u0026#34;utilsxx/lib/dsv_table.hpp\u0026#34; #include \u0026#34;utilsxx/lib/progress_bar.hpp\u0026#34; #include \u0026#34;utilsxx/lib/utc_time.hpp\u0026#34; #include \u0026#34;utilsxx/lib/constants.hpp\u0026#34; #include \u0026lt;iostream\u0026gt; int main() try { // 1. 读取配置 utilsxx::getoption gopt; gopt.add_options({\u0026#34;input\u0026#34;, \u0026#34;output\u0026#34;, \u0026#34;column\u0026#34;, \u0026#34;threshold\u0026#34;}, {true, true, true, false}); gopt.read_options(\u0026#34;config.txt\u0026#34;); gopt.check_mandatory(); std::string input = gopt.get_value\u0026lt;std::string\u0026gt;(\u0026#34;input\u0026#34;); std::string output = gopt.get_value\u0026lt;std::string\u0026gt;(\u0026#34;output\u0026#34;); std::string col_name = gopt.get_value\u0026lt;std::string\u0026gt;(\u0026#34;column\u0026#34;); double threshold = gopt.has_value(\u0026#34;threshold\u0026#34;) ? gopt.get_value\u0026lt;double\u0026gt;(\u0026#34;threshold\u0026#34;) : 0.0; // 2. 记录开始时间 utilsxx::UTC_TIME start; start.set_to_now(); std::cout \u0026lt;\u0026lt; \u0026#34;Start: \u0026#34; \u0026lt;\u0026lt; start.time_str() \u0026lt;\u0026lt; std::endl; // 3. 加载数据 utilsxx::dsv_table table; table.load_csv(input); std::cout \u0026lt;\u0026lt; \u0026#34;Loaded \u0026#34; \u0026lt;\u0026lt; table.row_number() \u0026lt;\u0026lt; \u0026#34; rows, \u0026#34; \u0026lt;\u0026lt; table.col_number() \u0026lt;\u0026lt; \u0026#34; columns\u0026#34; \u0026lt;\u0026lt; std::endl; // 4. 处理数据（带进度条） utilsxx::progress_bar bar(\u0026#34;Processing\u0026#34;, table.row_number()); for (int i = 1; i \u0026lt;= table.row_number(); ++i) { double val = table.cell\u0026lt;double\u0026gt;(i, table.name_index(col_name)); if (val \u0026gt; 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 \u0026lt;\u0026lt; \u0026#34;End: \u0026#34; \u0026lt;\u0026lt; end.time_str() \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;Elapsed: \u0026#34; \u0026lt;\u0026lt; end.diff_sec(start) \u0026lt;\u0026lt; \u0026#34; seconds\u0026#34; \u0026lt;\u0026lt; std::endl; return 0; } catch (const utilsxx::error_handler\u0026amp; 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。\n项目由浙江大学地球科学学院的张壹（Yi Zhang）开发和维护。\n结语 UtilsXX 不是一个追求\u0026quot;大而全\u0026quot;的框架，而是一个懂科研工作者痛点的实用工具箱。它将日常开发中最繁琐、最重复的工作封装成简洁的 API，让你可以把更多精力放在科学问题本身。无论你是处理地球物理观测数据、运行数值模拟，还是构建数据分析管道，UtilsXX 都能成为你可靠的 C++ 伙伴。\n项目地址：utilsxx\n作者：Yi Zhang (yizhang-geo@zju.edu.cn)\n机构：浙江大学地球科学学院\n","permalink":"https://geowisdom.com.cn/posts/resource/utilsxx/readme_utilsxx/","summary":"\u003ch2 id=\"引言\"\u003e引言\u003c/h2\u003e\n\u003cp\u003e在科学计算和工程应用领域，C++ 开发者常常面临一个尴尬的局面：标准库提供了基础的数据结构和算法，但面对具体的科研需求——比如读取一个带注释的 CSV 文件、在终端显示一个进度条、解析复杂的配置文件、或者监控一个耗时数小时的计算进程——往往需要自行编写大量\u0026quot;胶水代码\u0026quot;。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eUtilsXX\u003c/strong\u003e 正是为解决这些痛点而生。它是一个基于 C++17 的轻量级工具库，专为地球科学、物理学和工程计算领域设计。不同于 Boost 这样的\u0026quot;全能型\u0026quot;库，UtilsXX 聚焦于科研工作者日常最高频的需求：数据处理、文件读写、终端交互、配置管理、时间计算和物理常数。本文将全面介绍 UtilsXX 的设计理念、核心模块和使用方法，帮助你快速上手并融入自己的项目。\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"一设计理念为什么创建-utilsxx\"\u003e一、设计理念：为什么创建 UtilsXX？\u003c/h2\u003e\n\u003ch3 id=\"11-科研编程的特殊需求\"\u003e1.1 科研编程的特殊需求\u003c/h3\u003e\n\u003cp\u003e科学计算程序与传统软件工程有显著不同：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e数据驱动\u003c/strong\u003e：大量时间花在读取、清洗、转换各种格式的数据上\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e长时运行\u003c/strong\u003e：数值模拟可能持续数小时甚至数天，需要进度反馈和交互控制\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e参数密集\u003c/strong\u003e：算法通常有数十个可调参数，需要灵活的配置管理\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e跨平台协作\u003c/strong\u003e：代码需要在个人笔记本、工作站和超算集群上无缝运行\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e快速迭代\u003c/strong\u003e：研究思路变化快，代码需要易于修改和扩展\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"12-utilsxx-的设计原则\"\u003e1.2 UtilsXX 的设计原则\u003c/h3\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e原则\u003c/th\u003e\n          \u003cth\u003e实践\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003eHeader-Only 优先\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003e大部分组件为单头文件，直接 \u003ccode\u003e#include\u003c/code\u003e 即可使用\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003e零依赖或内嵌依赖\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003e核心模块无外部依赖；第三方库（如 nlohmann/json、toml11）直接内嵌\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003e类型安全\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003e大量使用模板和 \u003ccode\u003estatic_assert\u003c/code\u003e，编译期捕获类型错误\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003e异常友好\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003e统一的 \u003ccode\u003eerror_handler\u003c/code\u003e 错误处理机制，提供清晰的错误信息\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003e跨平台\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003eWindows、Linux、macOS 统一封装，终端操作透明适配\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003e科研导向\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003e内置地球物理常数、WGS84 参数、角度弧度转换等地学常用工具\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"13-与现有生态的关系\"\u003e1.3 与现有生态的关系\u003c/h3\u003e\n\u003cp\u003eUtilsXX 不是要取代谁，而是填补\u0026quot;标准库不够用、重型框架太重\u0026quot;之间的空白：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003evs 标准库\u003c/strong\u003e：补充了 CSV/JSON/TOML 解析、进度条、终端控制等缺失功能\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003evs Boost\u003c/strong\u003e：更轻量、更聚焦，学习成本更低\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003evs Python 生态\u003c/strong\u003e：让你在 C++ 中也能拥有类似 pandas（\u003ccode\u003edsv_table\u003c/code\u003e）、tqdm（\u003ccode\u003eprogress_bar\u003c/code\u003e）、argparse（\u003ccode\u003eget_option\u003c/code\u003e/\u003ccode\u003eCLI11\u003c/code\u003e）的体验\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003ch2 id=\"二项目概览与快速开始\"\u003e二、项目概览与快速开始\u003c/h2\u003e\n\u003ch3 id=\"21-目录结构\"\u003e2.1 目录结构\u003c/h3\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eutilsxx/\n├── lib/                    # 核心头文件库\n│   ├── vector.hpp          # 向量类型与算法\n│   ├── matrix.hpp          # 轻量级矩阵\n│   ├── dsv_table.hpp       # 表格数据处理\n│   ├── str_utils.hpp       # 字符串工具\n│   ├── file_utils.hpp      # 文件操作\n│   ├── get_option.hpp      # 配置参数解析\n│   ├── progress_bar.hpp    # 终端进度条\n│   ├── process_monitor.h   # 进程监控\n│   ├── utc_time.hpp        # UTC 时间处理\n│   ├── constants.hpp       # 数学与物理常数\n│   ├── term_utils.hpp      # 终端控制\n│   ├── json.hpp            # JSON 解析（nlohmann/json）\n│   ├── toml.hpp            # TOML 解析（toml11）\n│   ├── CLI11.hpp           # 命令行解析（CLI11）\n│   ├── indicators.hpp      # 高级进度条（indicators）\n│   ├── error_handler.hpp   # 错误处理\n│   └── src/                # 少量需要编译的源文件\n│       └── process_monitor.cpp\n├── demo/                   # 示例程序\n├── data/                   # 示例数据文件\n├── extra/                  # 第三方库示例\n├── CMakeLists.txt          # CMake 构建配置\n├── manager.sh              # 便捷管理脚本\n└── UtilsXXConfig.cmake.in  # CMake 包配置模板\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"22-编译安装\"\u003e2.2 编译安装\u003c/h3\u003e\n\u003cp\u003eUtilsXX 使用 CMake 构建，支持作为子项目或直接安装到系统：\u003c/p\u003e","title":"UtilsXX: 专为科学计算打造的 C++17 工具库"},{"content":"I needed to use Gnuplot a little bit over the last few days, mostly to create 2D line charts, and these are my brief notes on how to get started with Gnuplot. If you haven’t used it before, it’s an amazing tool for creating graphs and charts.\nJumping right in \u0026hellip;\nInstalling gnuplot To get started, you can use MacPorts or Homebrew to install Gnuplot on macOS systems:\nport install gnuplot brew install gnuplot Note that with Mac OS X Yosemite (10.10.x) running on an old Mac, I had to use this brew command instead:\nbrew install gnuplot --with-qt You’ll know that you need that command if you get this error message when you try to run a plot command inside the gnuplot command line:\nWARNING: Plotting with an \u0026#39;unknown\u0026#39; terminal. No output will be generated. Please select a terminal with \u0026#39;set terminal\u0026#39;. You can find more information about the need for this new brew command at this SO link.\ngnuplot: Sample data files The examples below use the following 2-column and 4-column data files:\n# sample 2-column data file # ------------------------- 1 1 2 4 3 9 4 16 5 25 6 36 7 49 8 64 9 81 10 100 My four-column file is named 4col.csv:\n1, 1, 2, 5 2, 4, 4, 10 3, 9, 6, 15 4, 16, 8, 20 5, 25, 10, 25 6, 36, 12, 30 7, 49, 14, 35 8, 64, 16, 40 9, 81, 18, 45 10, 100, 20, 50 Note that the columns in the first file are separated by whitespace, and the columns in the second file are separated by commas (a CSV file). The latest version of Gnuplot works with both formats without requiring you to specify a column-separator.\nStarting gnuplot Start Gnuplot from your Mac Terminal:\n$ gnuplot gnuplot\u0026gt; It prompts you with gnuplot\u0026gt; as shown, but I won’t show that prompt in the examples below.\nSimple plotting Plotting the data from a two-column file is easy:\nplot \u0026#39;2col.dat\u0026#39; This creates the following graph:\nKey points about this basic command:\nAssumes col1=x, col2=y; shows ’+’ at data points Does not connect points with a line Opens plot in an ’AquaTerm’ on Mac OS X From here you can do all sorts of fun things:\n# simple plotting plot \u0026#39;2col.dat\u0026#39; # assumes col1=x, col2=y; shows \u0026#39;+\u0026#39; at data points plot \u0026#39;2col.dat\u0026#39; with lines # connect points with a line plot \u0026#39;2col.dat\u0026#39; with linespoints # line and points plot \u0026#39;2col.dat\u0026#39; with points # just points (default) # plot a subset of the data plot[1:5] \u0026#39;2col.dat\u0026#39; with linespoints # plot the first 5 elements plot[3:7] \u0026#39;2col.dat\u0026#39; with linespoints # plot only elements 3 thru 7 # add a title to your line plot \u0026#39;2col.dat\u0026#39; with lines title \u0026#39;my curve\u0026#39; # this is really the line-title in the legend # map the columns to the x- and y-axes plot \u0026#39;2col.dat\u0026#39; using 1:2 # 1=x, 2=y (this is the default) plot \u0026#39;2col.dat\u0026#39; using 2:1 # 2=x, 1=y (reverse the graph) # abbreviations plot \u0026#39;2col.csv\u0026#39; u 1:2 w l title \u0026#39;Squared\u0026#39; # \u0026#39;u\u0026#39; - using, \u0026#39;w l\u0026#39; - with lines This command\nplot \u0026#39;2col.dat\u0026#39; with lines creates this plot:\nAnd this command:\nplot \u0026#39;2col.dat\u0026#39; with linespoints creates this plot:\nTitles, labels, legend, arrows You can adorn your plots with titles, labels, legend, arrows, and more:\nset title \u0026#39;Hello, world\u0026#39; # plot title set xlabel \u0026#39;Time\u0026#39; # x-axis label set ylabel \u0026#39;Distance\u0026#39; # y-axis label # labels set label \u0026#34;boiling point\u0026#34; at 10, 212 # key/legend set key top right set key box set key left bottom set key bmargin set key 0.01,100 set nokey # no key # arrow set arrow from 1,1 to 5,10 Multiple curves on one plot To show multiple curves on one plot, use the 4col.csv file:\n1, 1, 2, 5 2, 4, 4, 10 3, 9, 6, 15 4, 16, 8, 20 5, 25, 10, 25 6, 36, 12, 30 7, 49, 14, 35 8, 64, 16, 40 9, 81, 18, 45 10, 100, 20, 50 One curve using first two columns:\nplot \u0026#39;4col.csv\u0026#39; with lines Multiple curves:\nplot \u0026#39;4col.csv\u0026#39; using 1:2 with lines, \u0026#39;4col.csv\u0026#39; using 1:3 with lines plot \u0026#39;4col.csv\u0026#39; using 1:2 with lines, \u0026#39;4col.csv\u0026#39; using 1:3 with lines, \u0026#39;4col.csv\u0026#39; using 1:4 with lines The second command shown creates this chart:\nAdd a legend:\nplot \u0026#39;4col.csv\u0026#39; using 1:2 with lines title \u0026#39;Square\u0026#39;, \u0026#39;4col.csv\u0026#39; using 1:3 with lines title \u0026#39;Double\u0026#39; Use abbreviations:\nplot \u0026#39;4col.csv\u0026#39; u 1:2 w l title \u0026#39;Square\u0026#39;, \u0026#39;4col.csv\u0026#39; u 1:3 w l title \u0026#39;Double\u0026#39; Multiple formulas:\nplot sin(x) title \u0026#39;Sine Function\u0026#39;, tan(x) title \u0026#39;Tangent\u0026#39; Multiple graphs (multiplot) How to show multiple graphs in the output:\nset multiplot # multiplot mode (prompt changes to \u0026#39;multiplot\u0026#39;) set size 1, 0.5 set origin 0.0,0.5 plot sin(x), log(x) set origin 0.0,0.0 plot sin(x), log(x), cos(x) unset multiplot # exit multiplot mode (prompt changes back to \u0026#39;gnuplot\u0026#39;) That series of commands creates this chart:\nASCII plotting You can create ASCII plots in your Mac Terminal window:\nset terminal dumb plot cos(x) plot sin(x) w lines cos(x) w lines Plotting formulas It’s fun and easy to plot formulas with Gnuplot:\nplot sin(x) plot sin(x)/x plot cos(x) plot cos(x)/x plot sin(x) title \u0026#39;Sin\u0026#39;, tan(x) title \u0026#39;Tangent\u0026#39; Plot your own formulas:\nf(x) = sin(x) + tan(x) plot f(x) with points plot f(x) with filledcurves plot f(x) with filledcurves above x1 Grid, tickmarks, axis ranges, log It can be nice to have a grid on a chart, and it can also be nice to control the graph tickmarks, ranges, and origin:\n# grid set grid # ranges set autoscale # let gnuplot determine ranges (default) set xrange [1:10] set yrange [1:100] set xr [0.0:10.0] set yr [0:5] # tickmarks set xtics (1, 5, 10) set ytics (1, 25, 50, 75, 100) set xtic auto # set xtics automatically set ytic auto # set ytics automatically unset xtics set ytics 400 set ytics (0,200,400,600,800,1000,1200) set y2tics (-100,0,100) set mytics 4 set mxtics 5 set xzeroaxis lt -1 set x2zeroaxis lt -1 # logarithmic scale set logscale set logscale y Comment and column-separator characters Set gnuplot file comment character(s):\nset datafile commentschars \u0026#34;//\u0026#34; Set gnuplot file column separator:\n# see http://gnuplot.sourceforge.net/docs_4.2/node173.html set datafile separator \u0026#34;\\t\u0026#34; set datafile separator \u0026#34;,\u0026#34; set datafile separator \u0026#34;|\u0026#34; set datafile separator {\u0026#34;\u0026lt;char\u0026gt;\u0026#34; | whitespace} Run shell commands You can run shell commands from the Gnuplot command line:\n# run shell command !cat 2col.dat Session management When you work from the Gnuplot command line, you’re working in a session:\nreset # reset everything replot # re-plot your data after making changes Un-set the key/legend and re-plot:\nunset key replot Real-world example Here’s a quick real-world example. I had this dataset of my blood pressure and heart rate from yesterday:\n# time, sys, dia, hr 8.5, 112, 60, 52 9, 116, 73, 59 10.5, 127, 71, 58 11, 124, 69, 62 11.5, 117, 68, 60 12, 122, 73, 60 13, 121, 67, 62 15, 120, 78, 68 15.5, 134, 70, 96 16, 120, 72, 73 16.5, 114, 68, 72 22, 119, 69, 61 I then used this sequence of commands (including some trial and error that’s not shown):\nset grid set title \u0026#39;BP and Heartrate\u0026#39; set yrange [50:160] set xlabel \u0026#39;time (military)\u0026#39; set label \u0026#39;finished walk\u0026#39; at 15, 140 unset label set label \u0026#39;finished walk\u0026#39; at 15, 105 plot \u0026#39;bp-hr.dat\u0026#39; u 1:2 w lp t \u0026#39;systolic\u0026#39;, \u0026#39;bp-hr.dat\u0026#39; u 1:3 w lp t \u0026#39;diastolic\u0026#39;, \u0026#39;bp-hr.dat\u0026#39; u 1:4 w lp t \u0026#39;heartrate\u0026#39; to create this graph of my blood pressure and heart rate:\nIt would be better to put the blood pressure on the y-axis on the left, and the heart rate on the y-axis on the right, but I’m short on time, and haven’t learned how to do that yet.\nResources I mostly learned about Gnuplot from the following resources:\ngnuplot.sourceforge.net/demo/ http://www.cs.hmc.edu/~vrable/gnuplot/using-gnuplot.html http://research.physics.illinois.edu/ElectronicStructure/498-s97/comp_info/gnuplot.html http://people.duke.edu/~hpgavin/gnuplot.html http://gnuplot-tricks.blogspot.com/ http://lowrank.net/gnuplot/datafile-e.html http://www.helsinki.fi/~jalaaman/gnuplot/index.html Gnuplot Help Help commands:\nhelp help terminal Other notes:\nDon’t type a blank space after the line continuation character, ”\\\u0026quot; Your data may be in multiple data files More to come \u0026hellip;\n","permalink":"https://geowisdom.com.cn/posts/skills/a-collection-of-gnuplot-examples/","summary":"\u003cp\u003eI needed to use \u003ca href=\"http://www.gnuplot.info/\"\u003eGnuplot\u003c/a\u003e a little bit over the last few days, mostly to create 2D line charts, and these are my brief notes on how to get started with Gnuplot. If you haven’t used it before, it’s an \u003cem\u003eamazing\u003c/em\u003e tool for creating graphs and charts.\u003c/p\u003e\n\u003cp\u003eJumping right in \u0026hellip;\u003c/p\u003e\n\u003ch2 id=\"installing-gnuplot\"\u003eInstalling gnuplot\u003c/h2\u003e\n\u003cp\u003eTo get started, you can use MacPorts or Homebrew to install Gnuplot on macOS systems:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eport install gnuplot\nbrew install gnuplot\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNote that with Mac OS X Yosemite (10.10.x) running on an old Mac, I had to use this \u003ccode\u003ebrew\u003c/code\u003e command instead:\u003c/p\u003e","title":"A large collection of Gnuplot examples"},{"content":"Given a data file that looks something like this:\n// f(GHz) S211(dB) S212(dB) S213(dB) S214(dB) S215(dB) S216(dB) S217(dB) 0.100E+00 -0.743E-03 0.586E-03 -0.390E-02 -0.787E-02 -0.930E-02 -0.159E-01 -0.164E-01 0.200E+00 -0.354E-02 -0.616E-02 -0.161E-01 -0.270E-01 -0.374E-01 -0.537E-01 -0.673E-01 0.300E+00 -0.820E-02 -0.174E-01 -0.364E-01 -0.589E-01 -0.843E-01 -0.117E+00 -0.152E+00 0.400E+00 -0.147E-01 -0.331E-01 -0.649E-01 -0.104E+00 -0.150E+00 -0.206E+00 -0.272E+00 We want to make a plot. The X-axis of the plot is column 1 and the Y-axes are all the other columns. The software we\u0026rsquo;re going to use is gnuplot.\nStep 1\nIn gnuplot, comments are designated with the # sign. Edit the data file so that any comment lines looks like this:\n# f(GHz) W345(dB) W346(dB) W347(dB) W348(dB) W349(dB) W350(dB) W351(dB) Step 2\nMake an input file (I called this file tf.gp) with instructions for how to make the plot. Below is the file that I used.\n# Plot of file.dat # This command works for a linux computer. In linux, you need to specify the exact location of # the font you want to use set terminal png notransparent rounded giant font \u0026#34;/usr/share/fonts/msttcore/arial.ttf\u0026#34; 24 \\ size 1200,960 # nomirror means do not put tics on the opposite side of the plot set xtics nomirror set ytics nomirror # On the Y axis put a major tick every 5 set ytics 5 # On both the x and y axes split each space in half and put a minor tic there set mxtics 2 set mytics 2 # Line style for axes # Define a line style (we\u0026#39;re calling it 80) and set # lt = linetype to 0 (dashed line) # lc = linecolor to a gray defined by that number set style line 80 lt 0 lc rgb \u0026#34;#808080\u0026#34; # Set the border using the linestyle 80 that we defined # 3 = 1 + 2 (1 = plot the bottom line and 2 = plot the left line) # back means the border should be behind anything else drawn set border 3 back ls 80 # Line style for grid # Define a new linestyle (81) # linetype = 0 (dashed line) # linecolor = gray # lw = lineweight, make it half as wide as the axes lines set style line 81 lt 0 lc rgb \u0026#34;#808080\u0026#34; lw 0.5 # Draw the grid lines for both the major and minor tics set grid xtics set grid ytics set grid mxtics set grid mytics # Put the grid behind anything drawn and use the linestyle 81 set grid back ls 81 # Add line at -3db # Draw a line from the right end of the graph to the left end of the graph at # the y value of -3 # The line should not have an arrowhead # Linewidth = 2 # Linecolor = black # It should be in front of anything else drawn set arrow from graph 0,first -3 to graph 1, first -3 nohead lw 2 lc rgb \u0026#34;#000000\u0026#34; front # Put a label -3db at 80% the width of the graph and y = -2 (it will be just above the line drawn) set label \u0026#34;-3dB\u0026#34; at graph 0.8, first -2 # Create some linestyles for our data # pt = point type (triangles, circles, squares, etc.) # ps = point size set style line 1 lt 1 lc rgb \u0026#34;#A00000\u0026#34; lw 2 pt 7 ps 1.5 set style line 2 lt 1 lc rgb \u0026#34;#00A000\u0026#34; lw 2 pt 11 ps 1.5 set style line 3 lt 1 lc rgb \u0026#34;#5060D0\u0026#34; lw 2 pt 9 ps 1.5 set style line 4 lt 1 lc rgb \u0026#34;#0000A0\u0026#34; lw 2 pt 8 ps 1.5 set style line 5 lt 1 lc rgb \u0026#34;#D0D000\u0026#34; lw 2 pt 13 ps 1.5 set style line 6 lt 1 lc rgb \u0026#34;#00D0D0\u0026#34; lw 2 pt 12 ps 1.5 set style line 7 lt 1 lc rgb \u0026#34;#B200B2\u0026#34; lw 2 pt 5 ps 1.5 # Name our output file set output \u0026#34;tf.png\u0026#34; # Put X and Y labels set xlabel \u0026#34;Frequency, GHz\u0026#34; set ylabel \u0026#34;Transmission, dB\u0026#34; # Set the range of our x and y axes set xrange [1:10] set yrange [-30:5] # Give the plot a title set title \u0026#34;Transmission vs Frequency\u0026#34; # Put the legend at the bottom left of the plot set key left bottom # Plot the actual data # u 1:2 = using column 1 for X axis and column 2 for Y axis # w lp = with linepoints, meaning put a point symbol and draw a line # ls 1 = use our defined linestyle 1 # t \u0026#34;Test 1\u0026#34; = title \u0026#34;Test 1\u0026#34; will go in the legend # The rest of the lines plot columns 3, 5 and 7 plot \u0026#34;file.dat\u0026#34; u 1:2 w lp ls 1 t \u0026#34;Test 1\u0026#34;, \\ \u0026#34;file.dat\u0026#34; u 1:4 w lp ls 3 t \u0026#34;Test 2\u0026#34;, \\ \u0026#34;file.dat\u0026#34; u 1:6 w lp ls 5 t \u0026#34;Test 3\u0026#34;, \\ \u0026#34;file.dat\u0026#34; u 1:8 w lp ls 7 t \u0026#34;Test 4\u0026#34; # This is important because it closes our output file. set output Step 3\nStart gnuplot and run your file.\n$ gnuplot G N U P L O T Version 4.2 patchlevel 6 last modified Sep 2009 System: Linux 2.6.32-431.17.1.el6.x86_64 Copyright (C) 1986 - 1993, 1998, 2004, 2007 - 2009 Thomas Williams, Colin Kelley and many others Type \\`help\\` to access the on-line reference manual. The gnuplot FAQ is available from http://www.gnuplot.info/faq/ Send bug reports and suggestions to Terminal type set to \u0026#39;x11\u0026#39; gnuplot\u0026gt; load \u0026#39;tf.gp\u0026#39; gnuplot\u0026gt; quit Step 4\nThe file tf.png should have been created. Open it and take a look.\nNotes\nTo find out which numbers refer to which line types, thicknesses, etc. Run the test command in gnuplot.\n$ gnuplot G N U P L O T Version 4.2 patchlevel 6 last modified Sep 2009 System: Linux 2.6.32-431.17.1.el6.x86_64 Copyright (C) 1986 - 1993, 1998, 2004, 2007 - 2009 Thomas Williams, Colin Kelley and many others Type \\`help\\` to access the on-line reference manual. The gnuplot FAQ is available from http://www.gnuplot.info/faq/ Send bug reports and suggestions to Terminal type set to \u0026#39;x11\u0026#39; gnuplot\u0026gt; set terminal png Terminal type set to \u0026#39;png\u0026#39; Could not find/open font when opening font \u0026#34;arial\u0026#34;, using internal non-scalable font Options are \u0026#39;nocrop medium \u0026#39; gnuplot\u0026gt; set output \u0026#39;test.png\u0026#39; gnuplot\u0026gt; test gnuplot\u0026gt; set output gnuplot\u0026gt; quit Here is what the image looks like. On linux, it\u0026rsquo;s probably good to install the Microsoft fonts. I downloaded them here.\nUpdated: June 2014\n","permalink":"https://geowisdom.com.cn/posts/skills/pretty-plots-with-gnuplot/","summary":"\u003cp\u003eGiven a data file that looks something like this:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e//   f(GHz)       S211(dB)     S212(dB)     S213(dB)     S214(dB)     S215(dB)     S216(dB)     S217(dB)\n   0.100E+00   -0.743E-03    0.586E-03   -0.390E-02   -0.787E-02   -0.930E-02   -0.159E-01   -0.164E-01\n   0.200E+00   -0.354E-02   -0.616E-02   -0.161E-01   -0.270E-01   -0.374E-01   -0.537E-01   -0.673E-01\n   0.300E+00   -0.820E-02   -0.174E-01   -0.364E-01   -0.589E-01   -0.843E-01   -0.117E+00   -0.152E+00\n   0.400E+00   -0.147E-01   -0.331E-01   -0.649E-01   -0.104E+00   -0.150E+00   -0.206E+00   -0.272E+00\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWe want to make a plot. The X-axis of the plot is column 1 and the Y-axes are all the other columns. The software we\u0026rsquo;re going to use is \u003ca href=\"http://www.gnuplot.info/\"\u003egnuplot\u003c/a\u003e.\u003c/p\u003e","title":"Pretty Plots with Gnuplot"},{"content":"选择题 地磁场中占比最大的内源稳定场为____，它代表了地磁场空间分布的主要特征。\nA 大陆磁场 B 中心偶极子场 C 轴向地心偶极子场 D 磁异常 磁赤道处，地下球体产生的△T异常在中心主剖面上的特征为____。\nA 轴对称曲线 B 点对称曲线 C 反对称曲线 D 不对称曲线 一个通过重心绕水平轴自由转动的磁针，令其水平轴垂直于磁子午面，磁针在磁赤道位置的静止状态为____。\nA 磁针S极向下 B 磁针N极向下 C 磁针水平 D 磁针方向任意 质子磁力仪测量的读数值和经过各项校正计算所得的磁异常分别是指____。\nA |$\\vec{T}$|和 △T B |$\\vec{T_a}$|和 △T C △T 和|$\\vec{T_a}$| D |$\\vec{T_a}$|和△Z E T|和Za 磁法勘探野外工作至少需要____以上仪器同时观测。\nA 1台 B 2台 C 3台 在地球表面上任意一点的重力等于该点____。\nA 重力位的一阶导数 B 重力位的梯度 C 重力位 总强度磁异常ΔT是地磁场总强度矢量与正常地磁场矢量的____。\nA 矢量和 B 矢量差 C 模量差 D 模量和 磁法勘探野外工作方法中，若仪器读数显示不正常（如正常磁场47500nT，显示10000nT左右），且noise大于10nT，则可能是下述哪种原因引起的____。\nA 探头方向不对 B 探头接线处松动或断裂 C 电源电压不足 在地球表面上任意一点所受到的全部地球质量对它产生的引力方向是____。\nA 指向该点处的（铅）垂线方向 B 指向地心的 C 不确定 联系磁位函数与引力位函数的 Possion公式成立的条件是：在给定区域内____。\nA 密度和磁化强度均匀不变 B 密度可以是空间函数 C 磁化强度矢量可以是空间函数 D 密度和磁化强度矢量都是空间函数 大范围区域布格重力异常形态____。 A 与地形呈镜像 B 与莫霍面呈镜像 C 与地形形态相似 质子旋进磁力仪测量的物理量是____。 A 磁场强度 B 磁感应强度 C 磁化强度 D 磁化率 在频率域磁异常处理转换中，起低通滤波作用的处理转换是____。 A 向下延拓 B 向上延拓 C 垂向导数 D 化磁极 中国及其毗邻海区布格重力异常变化的总趋势是由东向西____。 A 逐渐减小 B 逐渐增大 C 无规律变化 D 不变 磁极处的垂直磁场强度与磁赤道处的水平磁场强度有如下近似关系为____。 A 相等 B 没有关系 C 2倍 D 3倍 空间重力异常（$\\Delta g_{fa}$）与布格重力异常的区别是没有做____。 A 地形校正 B 高度校正 C 中间层校正 D 正常场校正 两个相邻的重力等位面____。 A 会相切，不会相交 B 不会相切，不会相交 C 会相切，也会相交 D 会相交，不会相切 自激发电机假说认为地磁场起源于地球的____。 A 地壳 B 地幔 C 外核 D 内核 罗盘的指北针指向____。 A 地磁南极 B 地理北极 C 地磁北极 D 地理南极 如果一台重力仪的灵敏度很高，那么它的精度____。 A 一定很高 B 一定不高 C 不一定很高 球体的△T磁异常的极大值随着埋深的____次方衰减。 A 1 B 2 C 3 D 4 在磁法勘探中，二度磁性地质体的有效磁化强度是指____。 A 感应磁化强度 B 剩余磁化强度 C 总磁化强度 D 总磁化强度在OXZ平面内的投影 磁法勘探中日变站的最大控制范围要依据____来确定。 A 磁力仪一致性精度 B 工区地形确定 C 磁异常总精度 D 磁异常检查点个数 磁力仪一致性试验，采用____法。 A 单次观测 B 往返重复观测 C 同向双次观测 D 三重小循环观测 在地球表面，重力随着高度增加而减小，从地表向下随着深度的增加，重力通常____。 A 增大 B 减小 C 不变 下列关于大地水准面的说法正确的是____。 A 大地水准面是一个真实存在的曲面 B 大地水准面上各处的重力值相等 C 大地水准面上各处的重力位相等 D 大地水准面是一个虚构的规则椭球面 沉积岩原生剩磁的主要成因是____。 A 化学剩余磁性 B 碎屑剩余磁性 C 等温剩余磁性 D 热剩余磁性 总场磁异常ΔT在____的条件下，可以看成是磁异常矢量在地磁场方向上的投影分量。 A Ta \u0026lt;\u0026lt; T0 B ΔΤ\u0026lt; Τa C Ta \u0026lt;= To 在赤道附近的磁性地质体，受地磁场作用发生磁化时，所产生的磁异常？____ A 只有水平分量 B 只有垂直分量 C 有水平分量和垂直分量 在地形起伏的测区进行重力勘探时，测量数据经过各项改正（包括地形校正）后，对所获得的剩余布格重力异常采用哪种反演方式更符合实际？____ A 平面三维反演 B 起伏地形三维反演 关于岩矿石的密度，下列说法正确的是____。 A 岩石的密度与其所承受的压力无关 B 岩浆岩的密度通常小于沉积岩的密度 C 区域变质岩的变质程度越高则密度越大 重力异常的实质是地质体的____。 A 剩余密度产生的引力场强度在垂向上的分量 B 剩余质量产生的引力场强度在垂向上的分量 C 剩余质量所产生的引力场强度 D 万有引力和惯性离心离的合力 根据物质磁性分类，石油属于____。 A 顺磁质 B 抗磁质 C 铁磁质 使用磁法勘探寻找未爆炸弹，发现探测区域内有很多磁偶极子异常，但是方向各异，这可能是因为？____ A 未爆炸弹以剩余磁性为主导 B 探测区域存在强烈变化的外部地磁场 C 未爆炸弹埋藏深度各不相同 D 未爆炸弹尺寸各不相同 1 g.u. = ____mGal A 0.01 B 0.1 C 1.0 D 10 能否利用重力高度校正公式计算不同高度上的重力异常变化值____。 A 可以 B 不可以 已知测点海拔高度高于基点，当进行高度校正时，如果使用的高度小于实际高度时，会使求得的布格重力异常____。 A 变大 B 变小 C 无法确定 D 没有影响 在北半球，对磁异常进行化极处理后，磁异常的正值区域往哪边偏移？____ A 东 B 南 C 西 D 北 正常重力值在赤道处最小，在两极处数值最大，相差约____。 A 500 g.u. B 5000 mGal C 500 mGal 居里点是铁磁性物质的铁磁性转变为____的温度点。 A 逆磁性 B 顺磁性 C 反磁性 D 无磁性 重力异常最不可能出现于如下哪种地质现象？____ A 金属矿体 B 地下水过度开采 C 地面塌陷 D 断层破碎带 欧拉反褶积方法应用在重力数据处理当中，是用于计算____。 A 场源位置 B 场源形状 C 场源物性 在对相对重力变化进行地形改正时，高山（正地形）和沟谷（负地形）的地形影响是____。 A 正的 B 负的 C 有正有负 地球固体潮引起的重力变化幅值可达到____。 A ±0.1 mGal B +1.0 mGal C ±10 mGal 探测地下目标体的最基本条件是____ A 只要目标体有非零的物理属性 B 只要目标与围岩有足够强的物性差异 C 只要目标体有两个互不相同的物性 D 只要目标体尺寸足够大 根据布格校正公式分析其误差来源可知，造成与地形起伏相关的虚假异常主要是由于____。 A 高程测量不准而引起的 B 中间层密度不准而引起的 C 高程和中间层密度不准所共同引起的结束 下面属于地磁场短期变化的是____。 A 磁扰 B 日变 C 磁暴 D 磁极倒转 地球内部的古登堡面是____分界面 A 地幔与地核 B 地壳与地幔 C 上地幔与下地幔 D 内核与外核 重力勘探是基于岩矿石的____差异，通过观测重力场随空间、时间的变化规律来研究地球内部构造及寻找矿产能源的。 A 弹性 B 磁性 C 电性 D 密度 大地测量学中一般用____指代地球形状。 A 水准面 B 旋转椭球面 C 大地水准面 D 大地基准面 地磁场的等偏线共有____个汇聚点 A 0 B 2 C 4 D 8 重力异常向上延拓有利于突出以下哪种异常特征？ A 深部异常特征； B 浅部异常特征； C 局部异常特征； D 以上都不符合 一个区域的均衡重力异常大于零，意味着该地区： A 补偿不足，具有下降趋势； B 补偿不足，具有上升趋势； C 过补偿，具有上升趋势； D 过补偿，具有下降趋势 磁异常化极处理的目的是 A 将ΔT磁异常转换成Za磁异常； B 将不对称的磁异常转换成对称的磁异常； C 将倾斜磁化的磁异常转换成垂直磁化的磁异常。 大陆地壳的密度一般比海洋地壳的密度。 A 更小 B 更大 C 无显著差异 D 无法判断 对岩（矿）石磁性起决定性作用的是矿物的____含量。 A 磁铁矿 B 赤铁矿 C 钛铁矿 据2019年《Nature》杂志论文报道，出于未知原因，地球磁场的北极近期正快速偏离加拿大，直线扑向西伯利亚。为了应对这一变化，全球范围内地磁专家将共同商定新版的世界 磁场模型。地球磁极的漂移体现了地球磁场的____ A 长期变化 B 短期变化 C 扰动变化 在地球表面的某位置，发现能自由转动的小磁针静止时必竖直向下，这个位置是____ A 地球赤道和纬度45度附近 B 地理北极和纬度45度附近 C 地理南极和纬度45度附近 D 地理北极和地理南极附近 日月对地球的潮汐效应造成的周期性重力变化的幅值和下列哪个数值接近？ A 20 x10-5m/s2 B 2x10-5m/s2 C 0.2x10-5m/s2 D 0.02 x10-5m/s2 有效磁化强度倾角与地磁倾角的关系可表述为____: A 有效磁化强度倾角≥地磁倾角 B 有效磁化强度倾角≤地磁倾角 C 以上皆有可能 地球重力场模型指将重力场展开为球谐函数后的一组系数，因此也称位系数模型。位系数中二阶项与下列哪个量直接相关： A 地球质心位置 B 地球转动惯量 C 地球总质量 D 地球赤道的重力 一个区域地壳平均厚度35km，如果区域内某处A点的布格重力异常是100x10-5m/s2，A处的地壳厚度大约是。 A 30 km B 35 km C 40 km D 45 km 在中国大陆地区，地磁异常有时候并不呈现典型的“南正北负”伴生异常特征，主要是因为什么的影响。 A 感磁 B 退磁 C 剩磁 地球的重力场在时间和空间上都存在微小变化，为确定重力场的变化，国际大地测量和地球物理联合会选择一个参考椭球的重力场作为参考场，亦称正常重力场。请问，该参考椭球的扁率最接近下述哪个数值： A 1/250 B 1/300 C 1/350 D 1/400 磁异常向上延拓的作用是： A 压制浅部异常而突出深部异常 B 压制深部异常而突出浅部异常 C 以上皆有可能 地球磁场可以用球谐分析来表示，通常表示地核部分磁场的球谐系数阶数为： A n\u0026lt;13 B n\u0026lt;3 C n\u0026gt;3 地磁场的干扰变化包括 A 磁场偏移； B 地磁脉动； C 磁湾； D 磁暴。 简单布格重力异常是观测值经过____得到的。 A 纬度校正； B 高度校正； C 中间层校正； D 局部地形校正 填空题 质子磁力仪是利用氢原子的____这一物理现象来进行地磁场测量的。 人们将平均海洋面顺势延伸到大陆所形成的封闭曲面称为____。 正常重力值只与计算点的____有关。 根据测量的物理量不同，重力测量可分为动力法和____两类。 古地磁研究中，古地磁场假设为轴向地心偶极子场，因此可以根据岩石标本实测的____推测其岩石生成时的古纬度。 重力基点网的观测方法包括：三重小循环观测、____、单向循环重复观测。 重力均衡假说主要有艾里假说和____假说。 磁日变改正是为了消除地磁场的____变化所做的改正。 在磁异常处理中，____的作用是为消除斜磁化的影响。 铁磁性物质的磁化强度与磁化场之间呈____关系。 对布格重力异常进行____校正后，获得均衡异常。 将重磁异常换算到观测平面以上的某个平面上的处理称为____。 重力仪静态试验的目的是了解仪器静态零点漂移是否呈____变化。 重磁野外测量过程中，检查点的观测原则是：一同三不同，即____。 空间重力异常（$\\Delta g_{FI}$）反映的是____。 岩矿石的剩余磁性主要包括：热剩余磁性、____、化学剩余磁性、粘滞剩余磁性和等温剩余磁性等。 重力加速度在地心处达到最____。 简答题 什么是正常重力场和地球重力场模型？正常重力场和地球重力场模型的分布具有哪些特征? 选择一个地磁场强度要素和一个地磁场方向要素，分别说明其空间分布特征？ 什么是国际地磁参考场？其参数每几年更新一次? 磁法勘探中，利用质子旋进磁力仪测量地磁场总强度时，基点的确定和测量有必要吗？请说明有必要或没有必要的理由。 什么是4D重力测量？列举两种该测量技术的应用。 什么是均衡异常？简述均衡补偿“过剩”与均衡补偿“不足”。 简述两种重磁异常分离方法原理（空间域和频率域各一种）。 为什么需要做磁异常化极处理？忽略岩石剩余磁性对磁异常化极有什么影响? 什么是固体潮？固体潮会影响精密测量数据精度，请列举至少2种需要做固体潮改正的精密测量。 分析布格重力异常在陆域高山与河谷地区的特征及其原因。 简要说明地磁日变的原因及其特点，如何在磁法勘探中消除地磁日变的影响？ 什么是磁异常模量？为什么要开展磁异 常模量反演? 在有起伏的自然表面进行重力测量，经各项校正后所获得的重力异常是大地水准面（或总基点所在水准面）上的异常还是原测点处的重力异常，详细阐明为什么？ 给出位场频率域数据处理与转换的优点和流程。 简述为什么要进行重力校正？ 试简述如何使用磁法区分地下两个埋深不同、水平位置不同、规模不同的磁性异常体。 地壳均衡理论中，艾里均衡理论、普拉特理论以及与维宁－曼尼兹均衡之间有哪些差异？ 简述布格重力异常及其物理意义。 什么是国际地磁参考场（IGRF）？如何采用高斯球谐分析方法建立国际地磁参考场（IGRF）？ 简述球谐系数的物理意义。 地磁场随空间、时间变化的特征，对磁法勘探工作的意义何在？ 地磁场的构成。 如何理解磁性差异是磁法工作的地球物理前提？ 剩余磁化强度的实际意义。 举例说明消磁作用对Mi方向的影响。 质子旋进式磁力仪测量外磁场的基本原理。 简要介绍磁法勘探仪器的发展过程与趋势。 应用质子磁力仪测定标本磁性参数的梯度方式与总场方式有何同异？ ","permalink":"https://geowisdom.com.cn/posts/resource/%E9%87%8D%E7%A3%81%E7%9F%A5%E8%AF%86%E9%A2%98%E5%BA%93/","summary":"\u003ch3 id=\"选择题\"\u003e选择题\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003e地磁场中占比最大的内源稳定场为____，它代表了地磁场空间分布的主要特征。\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eA 大陆磁场\u003c/li\u003e\n\u003cli\u003eB 中心偶极子场\u003c/li\u003e\n\u003cli\u003eC 轴向地心偶极子场\u003c/li\u003e\n\u003cli\u003eD 磁异常\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e磁赤道处，地下球体产生的△T异常在中心主剖面上的特征为____。\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eA 轴对称曲线\u003c/li\u003e\n\u003cli\u003eB 点对称曲线\u003c/li\u003e\n\u003cli\u003eC 反对称曲线\u003c/li\u003e\n\u003cli\u003eD 不对称曲线\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e一个通过重心绕水平轴自由转动的磁针，令其水平轴垂直于磁子午面，磁针在磁赤道位置的静止状态为____。\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eA 磁针S极向下\u003c/li\u003e\n\u003cli\u003eB 磁针N极向下\u003c/li\u003e\n\u003cli\u003eC 磁针水平\u003c/li\u003e\n\u003cli\u003eD 磁针方向任意\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e质子磁力仪测量的读数值和经过各项校正计算所得的磁异常分别是指____。\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eA |$\\vec{T}$|和 △T\u003c/li\u003e\n\u003cli\u003eB |$\\vec{T_a}$|和 △T\u003c/li\u003e\n\u003cli\u003eC  △T 和|$\\vec{T_a}$|\u003c/li\u003e\n\u003cli\u003eD |$\\vec{T_a}$|和△Z\u003c/li\u003e\n\u003cli\u003eE T|和Za\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e磁法勘探野外工作至少需要____以上仪器同时观测。\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eA 1台\u003c/li\u003e\n\u003cli\u003eB 2台\u003c/li\u003e\n\u003cli\u003eC 3台\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e在地球表面上任意一点的重力等于该点____。\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eA 重力位的一阶导数\u003c/li\u003e\n\u003cli\u003eB 重力位的梯度\u003c/li\u003e\n\u003cli\u003eC 重力位\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e总强度磁异常ΔT是地磁场总强度矢量与正常地磁场矢量的____。\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eA 矢量和\u003c/li\u003e\n\u003cli\u003eB 矢量差\u003c/li\u003e\n\u003cli\u003eC 模量差\u003c/li\u003e\n\u003cli\u003eD 模量和\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e磁法勘探野外工作方法中，若仪器读数显示不正常（如正常磁场47500nT，显示10000nT左右），且noise大于10nT，则可能是下述哪种原因引起的____。\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eA 探头方向不对\u003c/li\u003e\n\u003cli\u003eB 探头接线处松动或断裂\u003c/li\u003e\n\u003cli\u003eC 电源电压不足\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e在地球表面上任意一点所受到的全部地球质量对它产生的引力方向是____。\u003c/p\u003e","title":"重磁知识题库"},{"content":"此脚本可用于在平面图上绘制轨迹。例子为绘制LGD算法的迭代路径，数据如下（仅截取前10行）：\n0 20 20 20 20 1 21.2864 20.6638 21 21 2 21.9939 21.0072 23.198 23.3067 3 22.4359 21.2145 26.3405 27.2079 4 22.5849 21.2829 32.1681 37.2527 5 26.0376 22.8571 32.8254 37.0327 6 28.1978 23.6712 33.8682 36.4159 7 31.0379 24.6084 34.8022 35.6868 8 31.104 24.626 37.3802 33.666 9 32.2038 24.918 38.7026 32.4291 数据共5列，分别为序号、轨迹1x、轨迹1y、轨迹2x、轨迹2y。\n脚本 #!/usr/bin/env bash # 1. Create files needed in the loop cat \u0026lt;\u0026lt; \u0026#39;EOF\u0026#39; \u0026gt; pre.sh gmt begin pre gmt set FONT_ANNOT_PRIMARY=15p,Times-Roman,black gmt set FONT_LABEL=15p,Times-Roman,black gmt set MAP_GRID_CROSS_SIZE_PRIMARY=5p gmt set MAP_FRAME_PEN=thinnest,black gmt set MAP_TICK_LENGTH_PRIMARY=4p/2p gmt grd2cpt data/gauss_model.nc -Clapaz -R0/100/0/100 -Z -D gmt grdimage data/gauss_model.nc -R0/100/0/100 -Bxag+l\u0026#34;x (m)\u0026#34; -Byag+l\u0026#34;y (m)\u0026#34; -JX15c/15c -X4.5c -Y1.5c gmt end EOF # 2. Set up the main frame script cat \u0026lt;\u0026lt; \u0026#39;EOF\u0026#39; \u0026gt; main.sh gmt begin # Plot smooth blue curve and dark red dots at all steps so far gmt convert data/lgd_trace.txt -qi0:${MOVIE_FRAME} \u0026gt; data.txt gmt plot data.txt -W0.05p,white -R0/100/0/100 -JX15c/15c -X4.5c -Y1.5c -i1,2 gmt plot data.txt -Sc0.05i -Gred -i1,2 gmt plot data.txt -W0.05p,yellow -R0/100/0/100 -JX15c/15c -i3,4 gmt plot data.txt -Sc0.05i -Gblack -i3,4 gmt end EOF # 3. Run the movie gmt movie main.sh -Sbpre.sh -Cxga -Tdata/lgd_trace.txt -Vi -D5 -Zs -Nlgd_anim -Fmp4 动画 您的浏览器不支持视频标签 查看完整脚本。\n","permalink":"https://geowisdom.com.cn/posts/skills/%E4%BD%BF%E7%94%A8gmt%E5%9C%A8%E5%B9%B3%E9%9D%A2%E5%9B%BE%E4%B8%8A%E7%BB%98%E5%88%B6%E8%BD%A8%E8%BF%B9%E5%8A%A8%E7%94%BB/","summary":"\u003cp\u003e此脚本可用于在平面图上绘制轨迹。例子为绘制LGD算法的迭代路径，数据如下（仅截取前10行）：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-text\" data-lang=\"text\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e0 20 20 20 20\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e1 21.2864 20.6638 21 21\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e2 21.9939 21.0072 23.198 23.3067\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e3 22.4359 21.2145 26.3405 27.2079\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e4 22.5849 21.2829 32.1681 37.2527\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e5 26.0376 22.8571 32.8254 37.0327\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e6 28.1978 23.6712 33.8682 36.4159\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e7 31.0379 24.6084 34.8022 35.6868\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e8 31.104 24.626 37.3802 33.666\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e9 32.2038 24.918 38.7026 32.4291\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e数据共5列，分别为序号、轨迹1x、轨迹1y、轨迹2x、轨迹2y。\u003c/p\u003e\n\u003ch4 id=\"脚本\"\u003e脚本\u003c/h4\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/usr/bin/env bash\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 1. Create files needed in the loop\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecat \u003cspan style=\"color:#e6db74\"\u003e\u0026lt;\u0026lt; \u0026#39;EOF\u0026#39; \u0026gt; pre.sh\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003egmt begin pre\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\tgmt set FONT_ANNOT_PRIMARY=15p,Times-Roman,black\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\tgmt set FONT_LABEL=15p,Times-Roman,black\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\tgmt set MAP_GRID_CROSS_SIZE_PRIMARY=5p\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\tgmt set MAP_FRAME_PEN=thinnest,black\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\tgmt set MAP_TICK_LENGTH_PRIMARY=4p/2p\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\tgmt grd2cpt data/gauss_model.nc -Clapaz -R0/100/0/100 -Z -D\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\tgmt grdimage data/gauss_model.nc -R0/100/0/100 -Bxag+l\u0026#34;x (m)\u0026#34; -Byag+l\u0026#34;y (m)\u0026#34; -JX15c/15c -X4.5c -Y1.5c\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003egmt end\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003eEOF\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 2. Set up the main frame script\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecat \u003cspan style=\"color:#e6db74\"\u003e\u0026lt;\u0026lt; \u0026#39;EOF\u0026#39; \u0026gt; main.sh\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003egmt begin\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t# Plot smooth blue curve and dark red dots at all steps so far\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\tgmt convert data/lgd_trace.txt -qi0:${MOVIE_FRAME} \u0026gt; data.txt\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\tgmt plot data.txt -W0.05p,white -R0/100/0/100 -JX15c/15c -X4.5c -Y1.5c -i1,2\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\tgmt plot data.txt -Sc0.05i -Gred -i1,2\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\tgmt plot data.txt -W0.05p,yellow -R0/100/0/100 -JX15c/15c -i3,4\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\tgmt plot data.txt -Sc0.05i -Gblack -i3,4\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003egmt end\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003eEOF\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 3. Run the movie\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egmt movie main.sh -Sbpre.sh -Cxga -Tdata/lgd_trace.txt -Vi -D5 -Zs -Nlgd_anim -Fmp4\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch4 id=\"动画\"\u003e动画\u003c/h4\u003e\n\n\n\n\n\n\n\u003cvideo \n  width=\"700\" \n  controls\n  \n  \n\u003e\n  \u003csource src=\"lgd_anim.mp4\" type=\"video/mp4\"\u003e\n  您的浏览器不支持视频标签\n\u003c/video\u003e\n\u003cp\u003e查看\u003ca href=\"plot_trace_mp4.sh\"\u003e完整脚本\u003c/a\u003e。\u003c/p\u003e","title":"使用GMT在平面图上绘制轨迹动画"},{"content":"目的 分析与测试带动量的LGD方法的搜索效率与算法特性。\n算法分析 LGD算法的核心是步长随机的梯度下降法，其中步长取值符合莱维分布。这样做带来的好处主要有两个：\n因为步长的最大取值为无限大，因此理论上算法具有全局收敛性。给定足够的搜索次数，算法一定会发现全局最优解； 搜索过程顺带对解空间的形态（起伏）进行了探索，因此可利用搜索路径的统计信息给出解的不确定度。 但也有一个缺点：由于在地球物理反演中一般会采用正则化的方法改善反演问题的奇异性，因此一些解空间的凹性质并不突出，这使得LGD在大多数迭代步骤中下降量不足。为了增加LGD的下降效率，同时保留其优点。考虑到Adam这类的最优化方法具有良好的下降效率和一定的全局最优性。因此计划在LGD算法中引入类似的动量概念，提升迭代效率。 下面是带有动量的LGD算法的简单分析：\n// 这里我们考虑一个二维最优化问题 // 首先计算归一化的迭代方向（这使得对动量和梯度模的直接统计没有了意义） // 迭代方向乘步长得到迭代矢量（这是我们真正想记录的量，它直接决定了迭代路径） direct_mod = sqrt(g2[0]*g2[0] + g2[1]*g2[1]); g2[0] = levy_length*g2[0]/direct_mod; g2[1] = levy_length*g2[1]/direct_mod; // 计算修正系数 mt *= beta_m; vt *= beta_v; // 利用指数平均对动量与梯度模进行估计（有偏的） m[0] = (beta_m*m[0] + (1.0 - beta_m)*g2[0]); m[1] = (beta_m*m[1] + (1.0 - beta_m)*g2[1]); v[0] = (beta_v*v[0] + (1.0 - beta_v)*g2[0]*g2[0]); v[1] = (beta_v*v[1] + (1.0 - beta_v)*g2[1]*g2[1]); // 对动量与梯度模进行校正 mhat[0] = m[0]/(1.0 - mt); mhat[1] = m[1]/(1.0 - mt); vhat[0] = v[0]/(1.0 - vt); vhat[1] = v[1]/(1.0 - vt); // 利用动量与梯度模进行迭代（此处不再需要乘以步长了） x2[0] = x2[0] - mhat[0]/(sqrt(vhat[0]) + 1e-8); x2[1] = x2[1] - mhat[1]/(sqrt(vhat[1]) + 1e-8); 模型试验 对比二维非凸问题的搜索过程（见下图，其中白色为LGD_Momentum的搜索路径，黑色为LGD搜索路径，红色为Adam算法的搜索路径），在实验过程中，两种LGD算法每次迭代的步长取值都是一样的，Adam算法的步长为LGD搜索步长的平均值，LGD算法的搜索次数为1000次。对比下图可见：\nAdam算法的搜索范围很小，直接陷入了局部极小值； LGD算法的搜索范围最大，路径总体呈放射状，搜索效率受到了一定的影响； LGD_Momentum算法的搜索范围较小一些，全局收敛性较LGD更弱，但更集中于极值附近，搜索的效率整体更高。 迭代路径对比图 问题 怎样更直观地对比LGD与LGD_Momentum的求解效率？ LGD_Momentum在地球物理反演中是否更优？ ","permalink":"https://geowisdom.com.cn/posts/research/lgd_momentum/","summary":"\u003ch4 id=\"目的\"\u003e目的\u003c/h4\u003e\n\u003cp\u003e分析与测试带动量的LGD方法的搜索效率与算法特性。\u003c/p\u003e\n\u003ch4 id=\"算法分析\"\u003e算法分析\u003c/h4\u003e\n\u003cp\u003eLGD算法的核心是步长随机的梯度下降法，其中步长取值符合莱维分布。这样做带来的好处主要有两个：\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e因为步长的最大取值为无限大，因此理论上算法具有全局收敛性。给定足够的搜索次数，算法一定会发现全局最优解；\u003c/li\u003e\n\u003cli\u003e搜索过程顺带对解空间的形态（起伏）进行了探索，因此可利用搜索路径的统计信息给出解的不确定度。\n但也有一个缺点：\u003cstrong\u003e由于在地球物理反演中一般会采用正则化的方法改善反演问题的奇异性，因此一些解空间的凹性质并不突出，这使得LGD在大多数迭代步骤中下降量不足\u003c/strong\u003e。为了增加LGD的下降效率，同时保留其优点。考虑到Adam这类的最优化方法具有良好的下降效率和一定的全局最优性。因此计划在LGD算法中引入类似的动量概念，提升迭代效率。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e\u003cstrong\u003e下面是带有动量的LGD算法的简单分析\u003c/strong\u003e：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// 这里我们考虑一个二维最优化问题\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// 首先计算归一化的迭代方向（这使得对动量和梯度模的直接统计没有了意义）\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// 迭代方向乘步长得到迭代矢量（这是我们真正想记录的量，它直接决定了迭代路径）\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edirect_mod \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e sqrt(g2[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e]\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eg2[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e g2[\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e]\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eg2[\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e]);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eg2[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e levy_length\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eg2[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e]\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003edirect_mod;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eg2[\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e levy_length\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eg2[\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e]\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003edirect_mod;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// 计算修正系数\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emt \u003cspan style=\"color:#f92672\"\u003e*=\u003c/span\u003e beta_m;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003evt \u003cspan style=\"color:#f92672\"\u003e*=\u003c/span\u003e beta_v;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// 利用指数平均对动量与梯度模进行估计（有偏的）\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003em[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (beta_m\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003em[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e (\u003cspan style=\"color:#ae81ff\"\u003e1.0\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e beta_m)\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eg2[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e]);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003em[\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (beta_m\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003em[\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e (\u003cspan style=\"color:#ae81ff\"\u003e1.0\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e beta_m)\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eg2[\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e]);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ev[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (beta_v\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003ev[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e (\u003cspan style=\"color:#ae81ff\"\u003e1.0\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e beta_v)\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eg2[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e]\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eg2[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e]);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ev[\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (beta_v\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003ev[\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e (\u003cspan style=\"color:#ae81ff\"\u003e1.0\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e beta_v)\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eg2[\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e]\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eg2[\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e]);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// 对动量与梯度模进行校正\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emhat[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e m[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e]\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e1.0\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e mt);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emhat[\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e m[\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e]\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e1.0\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e mt);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003evhat[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e v[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e]\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e1.0\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e vt);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003evhat[\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e v[\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e]\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e1.0\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e vt);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// 利用动量与梯度模进行迭代（此处不再需要乘以步长了）\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ex2[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e x2[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e mhat[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e]\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e(sqrt(vhat[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e]) \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1e-8\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ex2[\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e x2[\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e mhat[\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e]\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e(sqrt(vhat[\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e]) \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1e-8\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch4 id=\"模型试验\"\u003e模型试验\u003c/h4\u003e\n\u003cp\u003e对比二维非凸问题的搜索过程（见下图，其中白色为LGD_Momentum的搜索路径，黑色为LGD搜索路径，红色为Adam算法的搜索路径），在实验过程中，两种LGD算法每次迭代的步长取值都是一样的，Adam算法的步长为LGD搜索步长的平均值，LGD算法的搜索次数为1000次。对比下图可见：\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eAdam算法的搜索范围很小，直接陷入了局部极小值；\u003c/li\u003e\n\u003cli\u003eLGD算法的搜索范围最大，路径总体呈放射状，搜索效率受到了一定的影响；\u003c/li\u003e\n\u003cli\u003eLGD_Momentum算法的搜索范围较小一些，全局收敛性较LGD更弱，但更集中于极值附近，搜索的效率整体更高。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cfigure\u003e\n    \u003cimg loading=\"lazy\" src=\"lgd_trace.png\"\n         alt=\"图片无法显示\" width=\"400\"/\u003e \u003cfigcaption\u003e\n            迭代路径对比图\n        \u003c/figcaption\u003e\n\u003c/figure\u003e\n\n\u003ch4 id=\"问题\"\u003e问题\u003c/h4\u003e\n\u003col\u003e\n\u003cli\u003e怎样更直观地对比LGD与LGD_Momentum的求解效率？\u003c/li\u003e\n\u003cli\u003eLGD_Momentum在地球物理反演中是否更优？\u003c/li\u003e\n\u003c/ol\u003e","title":"LGD Momentum 算法测试"},{"content":"使用GMT程序包内的movie命令可以生成动画，更好地展示数据的动态变化。下面是一个利用该命令绘制动画的简单例子:\n您的浏览器不支持视频标签 1. 准备程序 首先编写动画背景图的生成脚本，包括生成绘图所需的数据等工作也可以在此完成（如果已有外部数据则不需要）。在实际运行中不需要手动执行此脚本，而是通过movie命令调用。\n# 使用heredoc方式将脚本保存到一个shell文件pre.sh # 在\u0026lt;\u0026lt;后添加-表示忽略tab制表符（注意不会忽略空格） cat \u0026lt;\u0026lt;- EOF \u0026gt; pre.sh # 使用gmt math生成数据，=号后接输出文件名称 # -T命令指定x坐标范围 # T SIND指定要计算的函数名称 gmt math -T0/360/10 T SIND = sin_point.txt gmt math -T0/360/1 T SIND = sin_curve.txt # 开始绘制底图 gmt begin # 使用gmt basemap绘制一张空的底图 具体的名称含义查看gmt文档 # -R指定坐标范围 -JX指定投影类型和地图大小 -X -Y平移图像 # -B指定坐标轴样式（包括ticks、labels和grids设置等） # --FONT_ANNOT_PRIMARY设置字体大小 gmt basemap -R0/360/-1.2/1.6 -JX22c/11.5c -X1c -Y1c \\ -BWSne+glightskyblue -Bxa90g90f30+u@. -Bya0.5f0.1g1 --FONT_ANNOT_PRIMARY=9p gmt end # 结束 EOF 2. 准备主程序 编写动画生成的主脚本。movie命令内置了一批常量和变量可供使用，其中常量包括：\nMOVIE_WIDTH 画布的宽度（整个电影画面）； MOVIE_HEIGHT 画布的高度（整个电影画面）； MOVIE_DPU 当前每单位点数； MOVIE_RATE 当前每秒帧数； MOVIE_NFRAMES 帧的总数。此外，如果使用-I名称，则列出的任何静态参数也将可用于所有脚本。 变量包括：\nMOVIE_FRAME 当前帧号（整数，例如136）； MOVIE_ITEM 格式化的帧号（字符串，例如000136）； MOVIE_NAME 当前帧的名称前缀（即prefix_MOVIE_ITEM）。此外，如果给定了时间文件，则还会设置变量MOVIE_COL0、MOVIE_COL1等，从而在时间文件中每列产生一个变量。如果时间文件有尾随文本，则可以通过变量MOVIE_TEXT访问该文本，如果单词拆分是由-T+w显式请求的，或者通过在-L或-P中选择单词标签隐式请求的），则尾随文本也会拆分为单个单词参数MOVIE_WORD0、MOVIE_WORDER1等。 # 使用heredoc方式将脚本保存到一个shell文件main.sh # 在\u0026lt;\u0026lt;后添加-表示忽略tab制表符（注意不会忽略空格） cat \u0026lt;\u0026lt; EOF \u0026gt; main.sh gmt begin # Plot smooth blue curve and dark red dots at all angle steps so far # -Q 快速数学计算 此处为将MOVIE_FRAME*10的结果赋值给last变量 last=$(gmt math -Q ${MOVIE_FRAME} 10 MUL =) # -qi0:${last} 读入前last行并传递给plot命令绘图 # -W1p,blue线宽1p，蓝色 其他设置与pre.sh内设置保持一致 gmt convert sin_curve.txt -qi0:${last} | gmt plot -W1p,blue -R0/360/-1.2/1.6 -JX22c/11.5c -X1c -Y1c # 使用plot命令绘制已经经过的点 # -Sc0.1i圆圈 -Gdarkred填充为暗红色 gmt convert sin_point.txt -qi0:${MOVIE_FRAME} | gmt plot -Sc0.1i -Gdarkred # Plot bright red dot at current angle and annotate # 使用plot命令绘制当前经过的点 # -Sc0.1i圆圈 -Gred填充为红色 # \u0026lt;\u0026lt;\u0026lt; 是here-string 输入数据 # ${MOVIE_COL0} ${MOVIE_COL1} 坐标位置 gmt plot -Sc0.1i -Gred \u0026lt;\u0026lt;\u0026lt; \u0026#34;${MOVIE_COL0} ${MOVIE_COL1}\u0026#34; # 使用text命令绘制标签 # 使用printf生成标签 # -F设置字体 -N不截断字符串 -D设置标签位置 printf \u0026#34;0 1.6 a = %3.3d\u0026#34; ${MOVIE_COL0} | gmt text -F+f14p,Helvetica-Bold+jTL -N -Dj0.1i/0.05i gmt end EOF 3. 运行movie命令生成视频 # -S 添加前景GMT现代模式脚本的名称，该脚本将构建一个附加到所有帧的静态前景图覆盖。或者，提供正确画布大小的PostScript文件作为前景。 # -C 设置视频分辨率 # -T 设置帧数，从＜min＞/＜max＞/＜inc＞[+n]创建时间，或提供具有特定于帧的信息的文件(本例子的情况)。如果使用＜min＞/＜max＞/＜inc＞，则使用+n来指示＜inc\u0026gt;实际上是帧数。如果\u0026lt;timefile\u0026gt;不存在，则必须由通过-Sb给出的后台脚本创建。 # -V 更改运行信息的详细程度 # -D 设置电影显示帧率（帧/秒）[默认为24] # -Z 转换为电影后擦除目录\u0026lt;prefix\u0026gt;[仅保留带PNG的目录]。附加s以删除所有输入脚本（主脚本和通过-E、-I、-s的任何文件）。 # -N 同时创建主框架图（即输入动画的名称） # -F 设置动画的格式 gmt movie main.sh -Sbpre.sh -Chd -Tsin_point.txt -Vi -D5 -Zs -Nanim01 -Fmp4 4. 一些有用的信息 Canvas尺寸设置参考图 \u0026ldquo;MOVIE_WIDTH和MOVIE_HEIGHT参数反映画布尺寸。您可以使用常规的-X和-Y选项为预期的绘图设置逻辑原点[72p，72p]，并使用投影参数（-J）指示选定的绘图区域（绿色）。\u0026rdquo;\n更多movie命令的用法可在GMT网站上查看。\n查看完整的脚本\n","permalink":"https://geowisdom.com.cn/posts/skills/%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E7%9A%84gmt%E5%8A%A8%E7%94%BB%E4%BE%8B%E5%AD%90/","summary":"\u003cp\u003e使用GMT程序包内的\u003cem\u003emovie\u003c/em\u003e命令可以生成动画，更好地展示数据的动态变化。下面是一个利用该命令绘制动画的简单例子:\u003c/p\u003e\n\n\n\n\n\n\n\u003cvideo \n  width=\"700\" \n  controls\n  \n  \n\u003e\n  \u003csource src=\"gmt_ani_ex1.mp4\" type=\"video/mp4\"\u003e\n  您的浏览器不支持视频标签\n\u003c/video\u003e\n\u003ch4 id=\"1-准备程序\"\u003e1. 准备程序\u003c/h4\u003e\n\u003cp\u003e首先编写动画背景图的生成脚本，包括生成绘图所需的数据等工作也可以在此完成（如果已有外部数据则不需要）。在实际运行中不需要手动执行此脚本，而是通过\u003cem\u003emovie\u003c/em\u003e命令调用。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 使用heredoc方式将脚本保存到一个shell文件pre.sh\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 在\u0026lt;\u0026lt;后添加-表示忽略tab制表符（注意不会忽略空格）\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecat \u003cspan style=\"color:#e6db74\"\u003e\u0026lt;\u0026lt;- EOF \u0026gt; pre.sh\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t# 使用gmt math生成数据，=号后接输出文件名称\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t# -T命令指定x坐标范围\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t# T SIND指定要计算的函数名称\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\tgmt math -T0/360/10 T SIND = sin_point.txt\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\tgmt math -T0/360/1 T SIND = sin_curve.txt\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t# 开始绘制底图\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\tgmt begin\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t\t# 使用gmt basemap绘制一张空的底图 具体的名称含义查看gmt文档\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t\t# -R指定坐标范围 -JX指定投影类型和地图大小 -X -Y平移图像\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t\t# -B指定坐标轴样式（包括ticks、labels和grids设置等）\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t\t# --FONT_ANNOT_PRIMARY设置字体大小\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t\tgmt basemap -R0/360/-1.2/1.6 -JX22c/11.5c -X1c -Y1c \\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t\t-BWSne+glightskyblue -Bxa90g90f30+u@. -Bya0.5f0.1g1 --FONT_ANNOT_PRIMARY=9p\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\tgmt end\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t# 结束\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003eEOF\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch4 id=\"2-准备主程序\"\u003e2. 准备主程序\u003c/h4\u003e\n\u003cp\u003e编写动画生成的主脚本。\u003cem\u003emovie\u003c/em\u003e命令内置了一批常量和变量可供使用，其中常量包括：\u003c/p\u003e","title":"一个简单的GMT动画例子"},{"content":"Introduction Data visualisation is extremely important for communicating the results of your research, either in a journal or to the general public, and for analysing and learning more about the characteristics of your data and system (so-called “exploratory data analysis”). One of the most fundamental tools in data visualisation is the two-dimensional plot (or graph). This tutorial will cover the basics of two-dimensional data visualisation using a program called gnuplot; a program which allows you to create high-quality, visually-pleasing figures and undertake robust post-hoc data analysis.\nWhy gnuplot? It’s generally a good to separate data generation from data visualisation, so you can generate your data once (whether it comes from a simulation or experiment) and then try multiple different visualisation strategies without needing to re-run everything. Gnuplot is a standalone tool which takes data in a relatively simple format and can produce a wide-variety of figures: 2D or 3D, fancy or barebones and supports a lot of different output formats. Gnuplot is also highly scriptable: its syntax is concise and makes simple tasks simple, so it can be controlled entirely through command-line scripts. This is useful in a data visualisation program, as it makes it easy to exactly reproduce figures, even long after they have been initially generated - a feature which can save a lot of hair-tearing and frustration if you ever need to come back to a project after a long break. The final major feature of gnuplot is the ability to do curve-fitting, both linear and non-linear, with a relatively simple and intuitive syntax.\nAll of these features make gnuplot a very powerful tool for data visualisation and analysis. It is by no means the be-all and end-all (if you need to do a lot of complicated post-processing before plotting then you may be better served by using Python), but gnuplot is a good choice for a lot of workloads as it is simple enough to rapidly explore multiple visualisation options while still being capable of making publication-quality figures once you’ve decided what you want.\nThis guide covers the installation and usage of gnuplot, with a particular focus on 2D visualisation and data analysis. It is consists of a handful of sections detailing some of the more useful features of gnuplot, as well as some case-studies using real data to demonstrate how you might use these features in the real world. Finally, there’s a list of resources on data visualisation and analysis at the end, which are worth reading if you want to learn the fundamentals of data science and statistics.\nInstalling gnuplot Installation instructions depend on which operating system you’re using:\nWindows: pre-compiled binaries are available on the gnuplot Sourceforge repository (go to the folder for the latest version and look for the file ending in -win64-mingw.exe). Mac: install with Homebrew package manager, or get pre-compiled binaries from Northwestern University (binaries compiled on OS X 10.15.6 (Catalina)). Linux: use your distribution’s package manager (e.g. sudo apt-get install gnuplot on Ubuntu, sudo dnf install gnuplot on CentOS, Red Hat or Fedora). Furthermore, gnuplot is installed on most clusters, so you can use it remotely if all else fails. You’ll need to enable GUI-forwarding (commonly called X11-forwarding, after a common Linux GUI backend) on the cluster to if you want to interact with the plots you make (saving to an image file should always work). If the cluster you’re using supports it, you can pass the -X flag when you log in with SSH (so your login command would become something like ssh -X username@gadi.nci.org.au) which will allow you to run graphical applications like gnuplot on the cluster. Beware that not all clusters support this option and it will be extremely laggy as the graphics need to be sent over the internet to your computer, so use this option sparingly.\nGetting help I have tried to include most of what you’ll need for basic gnuplot use in this tutorial, but some commands will depend on which platform you’re using or might have too many configuration options to list without breaking the flow of the document. In these cases there are two main places to get help for gnuplot:\nGnuplot manual - gnuplot has an extensive manual, which serves more as a reference manual than a tutorial (it’s a good place to look things up, but not a great way to learn the basics). Gnuplot interactive help menu - type help at the gnuplot prompt to bring up a (verbose) help menu. You can also go straight to help on a specific command by typing help \u0026lt;command\u0026gt;, e.g. help plot or help plot using. This menu is available from inside an interactive gnuplot session and should be your first point of reference for unfamiliar commands or options. If you want to generate a specific type of plot which is not mentioned in this tutorial, have a look at the extensive demo scripts on the gnuplot sourceforge page. Their documentation is a little bit sparse, but the example scripts should give you a good jumping-off point which you can then modify for your own use. Plotting 2D functions As you might expect from the name, the most important command for 2D plotting in gnuplot is plot. plot takes many options and can create many different types of plots, so it’s worth familiarising yourself with a broad outline of what’s possible. I will provide example gnuplot commands and data sets throughout this section, as well as examples of the plots they produce; I encourage you to follow along and experiment with the commands and options as you read.\nFirst off, plot can take both both built-in and user-defined analytic functions to plot. For example, the function 2*sin(x) can be plotted by doing:\nf(x) = 2*sin(x) plot f(x) Where the first line defines a function called f(x) and the second line plots it. It is not strictly necessary to store the function in a variable before plotting it; you can just do plot 2*sin(x) if you prefer. If you run the above commands as-is, you should see something like the following figure appear on your screen:\nGnuplot has built-in support for a lot of functions, so check the help menus help plot functions and help expressions functions for a list of plotting options and built-in functions, respectively.\nPlotting data from a file Gnuplot also has the ability plot data which you supply to it in a file. Files must be plain-text (although some binary formats such as Fortran output are supported), and must be formatted into regular columns. For example, a (short) data file might look like:\n# Lines starting with a \u0026#34;#\u0026#34; are comments and will be ignored by gnuplot # x y 1 2 2 4 3 6 4 8 If if we save this file as data.dat, then we can plot its contents by typing plot \u0026quot;data.dat\u0026quot;. Note that the quotes are significant: the filename must be enclosed in quotes or gnuplot will treat it as a variable. The resulting plot should look something like:\nData files can contain multiple columns and you can specify which columns to plot with the using statement as a modifier to plot. For example, if we have a new data file (again saved as data.dat):\n# t x y 1 6 4 2 4 16 3 2 32 If we wanted to plot t vs y (leaving out the x column) then we would run the command plot \u0026quot;data.dat\u0026quot; using 1:3. If we don’t manually specify which columns to use, then plot will use the first two columns (equivalent to plot \u0026quot;data.dat\u0026quot; using 1:2).\nWe’re not limited to just plotting the data as it appears in the data file. We can also perform element-wise transformations on data columns, such as multiplying all elements of a column by a constant before plotting. In order to do this, we must enclose the whole mathematical statement in brackets, and preface each column number with a dollar-sign (“$”) to indicate that gnuplot should treat it as a column identifier rather than a regular number.\nFor example, if we have the data file above with three columns t, x and y and want to plot t vs x*y, then the relevant gnuplot command is plot \u0026quot;data.dat\u0026quot; using 1:($2*$3). This command will produce an output which looks similar to:\nErrors and uncertainties 2D plots can also contain error bars, which is important for visualising real-world data from experiments or sophisticated simulations. One way to do this is to calculate symmetric errors in the dependent variable (usually called y in examples), such as a confidence interval based on the assumption of normally distributed errors. In this case, we might want to plot x vs y ± delta_y. Given a data file with the following format:\n# x y delta_y 1 4 1.2 2 16 2.4 3 32 1.5 we can generate a plot with symmetrix error bars via the command plot \u0026quot;data.dat\u0026quot; using 1:2:3 with errorbars, which will produce a plot like the following:\nIt is also possible to specify separate min and max bounds on the uncertainties, as in the following file:\n# x y y_min y_max 1 4 3.9 4.2 2 16 15 17 3 32 20.2 33.1 We would then plot this with the command plot \u0026quot;data.dat\u0026quot; using 1:2:3:4 with errorbars, which will generate something like the following figure:\nPlots with multiple curves/data sets Gnuplot can produce plots with multiple curves/sets of data points residing on the same set of axes by providing multiple arguments to plot, separated by commas. The syntax is very similar to the basic plotting we’ve already seen, and supports the same options. For example, we if we wanted to plot two sets of data points from two different data files, the command would look something like plot \u0026quot;data1.dat\u0026quot; using 1:3, \u0026quot;data2.dat\u0026quot; using 1:2:3 with errorbars and produce output like:\nGnuplot will automatically generate a key (or legend) showing which points correspond to which data sets. It’s important to note that this only works when we pass multiple data sets to the same plot command. Each call to plot overwrites the existing figure, so the (superficially similar) set of commands:\n\\`\\`\\` plot \u0026#34;data1.dat\u0026#34; using 1:3 plot \u0026#34;data2.dat\u0026#34; using 1:2:3 with errorbars \\`\\`\\` will first plot the first data set, before erasing it and plotting the second, resulting in only one set of points being plotted. This is probably not what you want to do when comparing data.\nGetting your data in the right format As we’ve seen, gnuplot requires data to be in a relatively easy-to-parse format before you can work with it. It can not, for example, read data from a spreadsheet program such as Microsoft Excel, nor can it read from an output file generated by simulation software like LAMMPS or VASP. Fortunately, it’s usually fairly straightfoward to wrangle data into the right format. What follows are examples of some programs you might use and how to integrate their output with gnuplot. This list is a work in progress, so feel free to ask if there’s another program you’d like to see added to the list.\nMicrosoft Excel: Desktop app: follow the instructions on this page to export your data to a text file. By default, Excel will use comma-separated values (CSV) as the output format, but you can change this to use spaces in the “Export” menu (or set the separator to “,” in gnuplot). It’s also a good idea to use a hash symbol (“#”) for text/comments, which you can set in the “Export” menu. Office365 (web app): at the time of writing, the Office365 web app does not allow you to export data to a text file. You’ll either need to use the desktop app, or transfer your spreadsheet to Google Sheets and export from there. Google Sheets: within the spreadsheet containing the data you want to export, go to “File \u0026gt; Download \u0026gt; Tab-separated values (.tsv, current sheet)”, the file should download automatically. Configuring plots Plots have a lot of properties which can be modified on a per-data basis. The two most common options are to set a title for the data-set, which will appear in the key/legend by adding title \u0026quot;Some string\u0026quot; to the plot command. Can also toggle whether to plot disconnected points or with connected lines by adding with lines to the plot command (points are the default and doesn’t need to be specified). Can also specify linecolor (note the American spelling) individually for each line. As an example, we can do plot \u0026quot;data.dat\u0026quot; using 1:3 with lines title \u0026quot;My data\u0026quot;. Can see the whole list of modifiers in the help menu: help plot and help plot with.\nAxis/plot-wide properties like titles, labels and the location of the key/legend are modified for the whole plot at once via the set command. This command has the syntax set \u0026lt;property\u0026gt; \u0026lt;arguments\u0026gt;.\nSome useful options you’re likely to want to set are:\ntitle: Plot title. xlabel, ylabel: labels for x- and y-axes. grid: overlay a grid on the plot. xrange [\u0026lt;min\u0026gt;:\u0026lt;max\u0026gt;], yrange [\u0026lt;min\u0026gt;:\u0026lt;max\u0026gt;]: set range of x- or y-axes. autoscale: set whether or not to automatically scale the plot’s axes with the data range. This setting is enabled by default, but will be disabled if xrange or yrange are set. logscale: switch from linear to logarithmic scale plot. Can set logscale for only one axis at a time by doing set logscale y (default is to set for both x and y axes). Properties can be undone via the unset command. E.g. unset title removes the current plot title. If you need to go back to the default settings, then the command reset will unset all plot-wide properties.\nYou can also control where to send the plot, such as whether to plot on the screen or save to a file, by setting the terminal in your gnuplot session. “The terminal” is gnuplot’s terminology for any kind of device which can accept a graphical plot (although funky options like printing to ASCII text files are possible) and is distinct from the meaning of “terminal” you might be familiar with from using the command-line. Terminals in gnuplot can generally be split into two categories: interactive terminals and files. Most gnuplot installations will default to an interactive terminal, which might be called something like “x11” or “qt”, depending on the operating system. Gnuplot will print the terminal when you first load an interactive session (look for a message like Terminal type is now 'qt'), but you can also print the terminal type at any time via the command show terminal. Some terminals have extra options like resolution, so check the help menu help terminal \u0026lt;help\u0026gt; if something doesn’t look right.\nYou’ll need to change the terminal before you can send your plot to a file; this is achieved through the command set terminal \u0026lt;term\u0026gt;. A list of available terminals, along with a short description, can be found by typing set terminal without any options. File-based terminals also require you to set a filename for the plot via the command set output \u0026lt;filename\u0026gt; (this command has no effect for interactive terminals, so you should only run it after switching terminals.\nAs a concrete example, suppose we want to save one of our plots to an SVG (Scalable Vector Graphics) file called my_plot.svg. We’d first need to set the terminal to the right option, then set the output file, before finally plotting (or replotting) the data. The full sequence of commands will look like:\nset terminal svg set output my_plot.svg plot \u0026#34;data.dat\u0026#34; using 1:2:3:4 with errorbars It’s important to remember that modified properties only take effect once you plot the function. If you’ve already got a plot and want to change some properties, then you’ll need to run replot before the changes will take effect. This will reproduce the most recent plot command (including options like error bars), but any changes you’ve made to the plot-wide settings (like terminal or plot title) will take effect since this is a new plot.\nSome other useful configuration options are:\nYou can change which character gnuplot will use to separate entries in a data file (the default is whitespace). For example, if you wanted to plot data from a CSV file then you’d do: set datafile separator \u0026quot;,\u0026quot;. Set zero axes (i.e. dashed lines at x=0 and y=0) via set zeroaxis Can also use set xzeroaxis or set yzeroaxis for x or y lines only. The location of the key can be changed from the default (top right) with the command `set key `, where `` can contain some combination of `top`, `bottom`, `left`, `center` and `right`. For example, to set the key location to the top left of the plot, you\u0026rsquo;d do `set key top left`. Can also put it outside the plot by doing `set key right top outside` Can also make the spacing between key entries larger or smaller set key spacing 2 Keys can have titles set key title \u0026quot;The legend\u0026quot; Can set tics either explicitly set ytics (0, 1, 2, 3) or specify spacing set ytics 0.1 Can also set minor tic marks set mxtics 5, where the number indicates the number of minor tics to include between major ones set grid also respects minor tics Sometimes only want to have tics on one side of the plot, can do this with set ytics nomirror Arrows are useful to point out important bits of the graph. Need to manually specify the positions of both the label and the arrow. The label’s position starts at its bottom left. We’d first want to do set label \u0026quot;A label\u0026quot; at 0.5,0.5 and then do set arrow from 1.0,0.75 to 2.0,1.5 Rapidly varying functions can look jagged, since gnuplot only samples at a rate of 100 points per tic mark. Can be useful to increase this by doing set sample \u0026lt;num_samples\u0026gt; and then plotting (or replotting). NOTE: this only works for builtin functions and has no effect on data taken from a file. It can be annoying to have to set all the configuration options every time you want to plot something, especially if you have a common set of options you reuse for all plots. Fortunately, gnuplot has a way to carry configurations over between sessions - the startup configuration file. On Linux and Mac, this file is located in your home directory and is called ~/.gnuplot (you’ll have to create it if it doesn’t exist), while on Windows the file is called GNUPLOT.INI in the directory \u0026lt;root\u0026gt;\\\\Users\\\\\u0026lt;username\u0026gt; (where \u0026lt;root\u0026gt; is the drive letter Windows is installed on (usually C:)) [^1]. Gnuplot reads and executes the contents of this file at the start of every session, so you only need to specify the options once, manually changing them only when you want to do something special.\nCase study 1: molecular dynamics energies Let’s look at a concrete example using real-world data to tie together what we’ve learned so far. The data in this section comes from simulating a Lennard-Jones (LJ) fluid under shear flow in LAMMPS and outputting the temperature, kinetic energy and potential energy at each time-step. Here is a sample of the output data, which is saved in the file “MD_energy.dat”:\n#Step Temp KinEng PotEng 0 1.44 2.1589453 0 1 1.4399848 2.1589225 0 2 1.4399377 2.158852 0 3 1.439859 2.1587338 0 4 1.4397484 2.1585681 0 5 1.4396061 2.1583548 0 ... 100 1.1114163 1.6663104 0.43059789 ... 900 1.0989108 1.6475614 0.41621329 ... 998 1.1142942 1.6706251 0.46925806 999 1.1154457 1.6723515 0.4671758 1000 1.1188787 1.6774986 0.46154465 I’ve elided most of the points in the data set for readability, but if you want to download the whole set to follow along with this example, you can find it at this link: https://gist.github.com/emilyviolet/5c4fe0dcb369c5cdff06ccc658597120\nAs a first step, we might want to investigate how the energy of the system changes over time. This means we’ll want to plot time-step on the x-axis and the kinetic and potential energies on the y-axis, preferrably on the same set of axes. To do this in gnuplot, we’ll need:\nLine plots to make the trends easier to follow, Labels for the two lines (the key), and Plot-wide labels: title and axis labels. We’ll also introduce the interface for using scripts to automate plotting in this example, since this makes it easier to replicate and compare plots than doing everything interactively.\nLet’s start with plotting the energies as two different curves. Save the following script to a text file with the title MD_energy.gp:\nset xlabel \u0026#34;Timestep\u0026#34; set ylabel \u0026#34;E (LJ-units)\u0026#34; set title \u0026#34;Kinetic vs potential energy for LJ fluid under shear\u0026#34; # Need to escape line breaks with backslash, since gnuplot expects plot # commands to appear on the same line plot \u0026#34;MD_energy.dat\u0026#34; using 1:3 with lines title \u0026#34;Kinetic energy\u0026#34;, \\ \u0026#34;MD_energy.dat\u0026#34; using 1:4 with lines title \u0026#34;Potential energy\u0026#34; We can tell gnuplot to read commands from this file by running the command load \u0026quot;MD_energy.gp\u0026quot;. Running commands from a file will generally produce the same plot as if you typed each of the lines in the script one after the other in an interactive session, so saving plot instructions in a script is simply a way to save the logic of generating a plot for easier re-use.\nRunning the above script will produce output which looks something like the following:\nThis is all well and good, but it doesn’t capture all the information we might want about the system: it would also be useful to include the total energy of the system on the same plot. Even though the data file doesn’t contain a column for the total energy at each time step, we know that the total energy is defined by E_total = kinetic + potential so we can get what we need by plotting the sum of columns 3 and 4. The command to do this is plot \u0026quot;MD_energy.dat\u0026quot; using 1:($3 + $4) title \u0026quot;Total energy\u0026quot;, and gives the following plot:\nThis gives a clear indication of the total energy of the system (e.g. it isn’t constant in an NVT ensemble), even though it is derived from existing data - it’s much easier to have gnuplot transform and visualise some relationship between data points than to do it in your head.\nFinally, notice that the kinetic and potential energy curves are quite spiky. This is an inherent feature of the data, but it might make it more difficult in some cases to see broad trends so it can be useful to smooth out the data. Gnuplot has the ability to do this via the smooth modifier to the plot command, and has several different smoothing schemes built in. There are several “statistical” smoothing options such as by generating a cumulative distribution function or ensuring the data are unique (with each x-value corresponding to at most one y-value), but the one we’re interested in here are the spline-based options, specifically smooth bezier. This option approximates the data with a Bezier curve (a set of piecewise polynomials), so provides a nonlinear trend-line fitted to noisy or spiky data.\nLet’s plot a pair of Bezier curves to smoothly the kinetic and potential energies as grey to make it clear that they are only an approximation of the real data. I’ve also decided to use dashed lines for the kinetic and potential energy to make the smoothed trend-lines easier to see. Finally, the key now has an extra item to identify the smoothed lines, so I’ve moved it to the center-top position to avoid overlapping with the plot.\nTo make this plot, we add a few new lines to the plotting script:\nset xlabel \u0026#34;Timestep\u0026#34; set ylabel \u0026#34;E (LJ-units)\u0026#34; set title \u0026#34;Kinetic vs potential energy for LJ fluid under shear\u0026#34; set key top center # Standard plot plot \u0026#34;MD_energy.dat\u0026#34; using 1:3 with lines title \u0026#34;Kinetic energy\u0026#34; dashtype 2, \\ \u0026#34;MD_energy.dat\u0026#34; using 1:4 with lines title \u0026#34;Potential energy\u0026#34; dashtype 2, \\ \u0026#34;MD_energy.dat\u0026#34; using 1:($3 + $4) with lines title \u0026#34;Total energy\u0026#34;, \\ \u0026#34;MD_energy.dat\u0026#34; using 1:3 with lines title \u0026#34;\u0026#34; smooth bezier linecolor \u0026#34;#595959\u0026#34; , \\ \u0026#34;MD_energy.dat\u0026#34; using 1:4 with lines title \u0026#34;\u0026#34; smooth bezier linecolor \u0026#34;#595959\u0026#34; and end up with the following plot:\nWe can pretty clearly see both the original, “real” data (the kinetic, potential and total energies) as well as the smoothed interpolation by Bezier curves. The curves are plotted in different colors so the smoothed data looks (in grey) qualitatively different to the base data (in color). It does add some level of visual clutter, though, so the decision on whether to plot the smoothed datawill depend on the specifics of your data set.\nBeware though: smoothing and interpolating the data can sometimes make underlying trends easier to see, but can also obscure real features or give the impression of trends which aren’t really there if used injudiciously. As with any visualisation technique, it’s important to think about the nature of the data you’re visualising (e.g. does the physics of the situation tell you anything about trends and relationships you should expect) before jumping in to it. See chapter 14 of the textbook by Wilke for a more detailed discussion of the benefits and downsides of this sort of visualisation technique.\nPlot styling and customisation Basic gnuplot looks quite spartan, but its strength is that it’s highly customisable - you can make your gnuplot figures follow pretty much any style you like. In this section, we’ll look at changing line styles, colors and widths in gnuplot.\nFirst, it’s important to note that the available style options depend on the version of gnuplot you have installed, as well as the terminal to which you’re sending the plot. The two ways to see what’s available are through the help menu option help set style, or by having gnuplot draw a so-called “test page” with the command test. On my computer (CentOS 8 Linux), the test page looks like this:\nAmong other things, this page shows the different line styles, point styles, line widths (abbreviated as lw), colors and so on, each labeled by an integer index. This index is used with plot with and set style to refer to a specific style. For example, if we wanted to plot some data points using style 5 (empty squares) we could do:\nplot \u0026#34;data.dat\u0026#34; with points pointtype 5 Colours If we wanted instead to use a specific line-color for our plot, we can use the linecolor (lc) attribute, which can take either a pre-defined color or an arbitrary hex-code. For example, if we wanted to plot a curve in “UQ Purple”, which is defined by the RGB components (81, 36, 122). The easiest way to use this color in gnuplot is to convert the RGB components to a hexadecimal code (“hex code”), which you can then use the syntax plot \u0026lt;data\u0026gt; with lines linecolor \u0026quot;#\u0026lt;hex\u0026gt;\u0026quot; (the hex code must be prefaced by either a “#” or “0x” and be enclosed in quotes).\nThe DuckDuckGo search engine has a convenient built-in tool to convert between RGB (or the alternative Hue-Saturation-Value (HSV) scheme) and hexadecimal representations, which can be found at this link. Feeding the RGB values of UQ Purple into this tool gives a hex code of 51247a, which we can now use in gnuplot. For example, to plot a sin curve with this color, we can now do plot sin(x) with lines linecolor rgb \u0026quot;#51247a\u0026quot;, which gives the following figure:\nCustomising gnuplot Gnuplot lets us store and read customisations from a file to re-use across plots. This is a good way to ensure consistency across multiple plots, especially if submitting to journals which require a certain style for figures. Gnuplot keeps an internal list of line styles which it cycles through when plotting lines without explicit style options. This list can be modified through the set style line \u0026lt;num\u0026gt; \u0026lt;options\u0026gt; command, which will override the default style for that line number. Once this is loaded, gnuplot will use line style 1 for the first data set in a plot, then line style 2 and so on. Have a look through the help option set style line for the available options.\nIf you save these options to a file, you can read it with the commands:\nload \u0026#34;style_file.gp\u0026#34; set style increment user One other important consideration is accessibility; ensuring your figures are accessible and interpretable by people with colorblindness or other visual impairments. This is an area which many people overlook, despite the fact that between 4-10% of the male population has some form of colorblindness (if you’ve given a conference presentation to a reasonably large audience, it’s almost guaranteed that at least one person in the audience has colorblindness).\nFortunately, much research and engineering work has been done on accessible graphics, so the best-practices are quite easy to follow. Here are the main points to keep in mind when visualising your data:\nConsider using line or fill patterns (e.g. dashed vs solid lines) to differentiate data sets, as this does not rely on color vision at all. Most output file types in gnuplot have an option to output to greyscale, which is more accessible to people with color-vision deficiency (almost by definition). For PNG and PDF output via the Cairo rendering library (a common option on Linux and Mac), this is enabled via the “mono” option when setting the terminal, e.g. set terminal pdfcairo mono. Use a colorblind-friendly palette for your graphs by default. This blog post has a great roundup of colorblind-friendly palettes. As we’ll see below, gnuplot even has some built in and ready to go. Avoid using red and green to differentiate between data sets, since red-green colorblindness is the most common form of color-vision deficiency. Unfortunately, some installations of gnuplot use red and green as the default color scheme, so it’s a good idea to change your default palette. Use thick lines for plots used in presentations and avoid using light line colors like yellow on a white background. Consider using a thick/large print font for labels and major axis tic marks. Even more fortunately, gnuplot has a built in option to use an accessible color scheme, albeit a poorly-advertised one. All you need to do is run the command set colorsequence podo to tell gnuplot to use this color scheme. If you want to use thicker lines for all plots in a figure then you can also use set termoption linewidth \u0026lt;width\u0026gt;, substituting \u0026lt;width\u0026gt; for your preferred thickness (I find 1.5 is a good default. It is tedious to set these options every time you want to plot something, so I recommend adding them to your ~/.gnuplot file (or GNUPLOT.INI if you’re using Windows) to make them the default settings. For example, my ~/.gnuplot file looks like:\nset colorsequence podo set termoption linewidth 1.5 This color scheme only includes eight colors and so cannot be used for plots containing more than eight different data sets. However, using too many different colors can actually make the visualisation harder to interpret, so if you need to have more than eight data sets then you’re probably better off using something other than colors (e.g. different line patterns) to differentiate between categories of data.\nOne thing to keep in mind that different colors will look better printed, while others are better suited to being viewed on screens (projectors are a different story yet again). It’s hard to give specific guidelines for all possible media, but some general rules are that you should use darker colors and thicker lines for figures which will be viewed on a projector compared to figures meant for paper or screens. Titles, axis labels and elements of the plot’s key should also use a larger font if they are to be used in a presentation, otherwise audience members at the back of the room (or who may have visual impairments) won’t be able to read them. Whatever you do, always make sure to see how the plot looks in the intended medium before publishing it; your audience/reviewers will thank you for it.\nFinally, I strongly recommend saving graphs to a vector graphics format if plots are to be included in papers, theses or presentations. This is because vector graphics formats can scale up or down to arbitrary resolution without getting blurry, whereas raster graphics formats like png turn into pixel soup if you make them too much larger than their original resolution. Vector graphics are the only way to ensure figures stay crisp at all resolutions without requiring enormous file sizes (e.g. no need to make a 4 megapixel png) and are well supported in most LaTeX styles.\nGnuplot (usually) supports svg, pdf, eps (encapsulated postscript) and LaTeX, depending on the platform. The best choice of format will depend on where you’re planning to use the graphics (e.g. a website will probably not support LaTeX graphics), but svg is usually a good default choice.\nCase study 2: bar charts Line and scatter graphs are very useful when visualising purely numeric data where we can impose some kind of ordering on the points (e.g. time-series data). This is not the only kind of 2D visualisation you’re likely to run into, however. Sometimes you need to visualise data where one variable represents categories of things, such as this case study which aims to visualise the performance of different C++ compilers on a standard suite of benchmarks.\nIn this case, we want to visualise how the speed of the generated code is affected by choice of compiler and compile-time options for a suite of 8 benchmarks. In this case, we have three variables to visualise: compiler (GCC or Intel, plus whether or not to enable vectorisation), benchmark (which is a small program designed to test a particular computing pattern), and run-time. We have two categorical variables and want to compare a numeric variable, so a bar chart is a good choice of visualisation.\nIn order to make a bar chart in gnuplot, we’ll need to introduce a few new concepts which aren’t (usually) present in line charts:\nThe visualisation needs to be boxes whose height is given by columns in a data file, rather than points or lines. The axis tic labels should be strings, rather than numbers. These strings should ideally be read from the same data file as the numeric data. We want to cluster related data boxes together, so that all results from each benchmark are more easily compared. The data file we’re drawing from needs to be in a slightly different format than we’re used to, as well. The data needs to be organised with labels for the x-axis in one column and the corresponding wall time data in the rest. Here’s the data we’ll be using in this case-study:\n# Walltime (s) # \u0026#34;Benchmark name\u0026#34; \u0026#34;gfortran\u0026#34; \u0026#34;gfortran+SIMD\u0026#34; \u0026#34;ifort\u0026#34; \u0026#34;ifort+SIMD\u0026#34; IS 1.23 1.34 1.53 1.36 EP 26.41 32.39 18.29 15.84 CG 40.76 47.26 46.76 46.55 MG 18.84 19.1 19.77 20.16 FT 43.53 43.87 39.76 40.82 BT 213.1 183.07 217.99 169.26 SP 145.23 144.26 148.58 145.46 LU 105.04 100.12 117.66 106.71 We can see immediately that the first column contains the names of the benchmarks in the test: “IS”, “EP”, “CG”, “MG”, “FT”, “BT”, “SP” and “LU” (if you’re interested in what the names mean, they’re abbreviations from NASA’s Parallel Benchmark Suite). Each of these benchmarks then has four run-times, which correspond to the time taken to execute the benchmark when it’s generated by the corresponding compiler. These will be our bars, color-coded by compiler type.\nThat’s the data, so what about the chart itself? Bar charts are qualitatively different than line charts, so we’ll need to tell gnuplot to treat the data differently than simple numeric data. This is achieved via the histogram data and plotting style, which we set via the following commands:\nset style data histogram set style histogram cluster set style fill solid The first of these commands tells gnuplot that the input data is organised by categories, the second sets the plot to use boxes rather than lines and that the data should be clustered by categories, and the third fills in the boxes with solid colors to differentiate the clusters.\nFinally, we need to plot the data and specify where to get the labels for the x-axis tics from. We can do this by adding an extra column to the plot command as follows:\nplot \u0026#34;benchmarks.dat\u0026#34; using 2:xtic(1) title \u0026#34;gfortran\u0026#34; which plots the run-time data in the second column, using the first column (the benchmark names) as the x-axis labels, and gives it the title gfortran in the key. This description is somewhat abstract, so here is the graph generated by the above command to more clearly show what it does:\nWe can add the data for the rest of the compilers by extending this plot command to plot each successive column, which will look something like the following script:\nset xlabel \u0026#34;Benchmark name\u0026#34; set ylabel \u0026#34;Walltime(s)\u0026#34; set title \u0026#34;Compiler benchmark for NPB Fortran benchmark suite\u0026#34; set key top left set style data histogram set style histogram cluster gap 1 # Leave a gap between successive clusters set style fill solid set boxwidth 0.9 plot \u0026#34;benchmarks.dat\u0026#34; using 2:xtic(1) title \u0026#34;gfortran\u0026#34;, \\ \u0026#34;benchmarks.dat\u0026#34; using 3:xtic(1) title \u0026#34;gfortran + SIMD\u0026#34; , \\ \u0026#34;benchmarks.dat\u0026#34; using 4:xtic(1) title \u0026#34;Intel fortran\u0026#34; , \\ \u0026#34;benchmarks.dat\u0026#34; using 5:xtic(1) title \u0026#34;Intel fortran + SIMD\u0026#34; The full output of this script is shown below:\nThis script produces a bar chart with all the properties we laid out at the start of this example: the data are clustered so that run-times for the same benchmark are all adjacent to one another, the results for each compiler are differentiated by the color of their bars, and the x-axis has appropriate labels.\nMulti-panel figures (multiplots) Gnuplot supports multi-panel figures (multiple separate plots and axes in one figure), which can be useful when visualising a large number of data sets, as plotting them all on the same set of axes can look busy and hard to interpret. It’s fairly easy to set up multiplots in gnuplot via the set multiplot layout \u0026lt;vertical,horizontal\u0026gt; command, where vertical and horizontal are the number of axes to plot in the vertical and horizontal sections, and thus control the layout of the figure.\nAs an example, the following set of commands generates a multiplot with two plots stacked on top of each other (a vertical layout):\nset multiplot layout 2,1 # Vertical layout with 2 plots plot sin(x), sin(2*x) plot cos(x), cos(2*x) Notice how each plot command is mapped to a different set of axes within the multiplot. If we wanted to make a layout with more plots, then we simply add in more calls to plot. Be careful though, as gnuplot will not warn you if you have more plot commands than sets of axes - it will simply wrap back around and start plotting on the existing axes, which looks extremely ugly. Consider the following script, which attempts to plot three things on two sub-plots:\n# Deliberately bad example, do not do this set multiplot layout 2,1 # Vertical layout with 2 plots plot sin(x), sin(2*x) plot tan(x) plot cos(x), cos(2*x) In addition to having too many lines on the top plot, the legend is completely broken as gnuplot has attempted to render two different keys in the same location at the same time. This problem is fairly easy to avoid, but highlights the need to take extra care with multi-plot figures.\nGnuplot attempts to automatically line-up the x-axes to make it easier to compare the two plots, but it cannot do the same with the y-axes (since the plot has a vertical layout), making direct comparison of amplitudes difficult. For this reason, it’s usually a good idea to enable axis grids on multiplots as they make it possible to line up exact x-y-values and compare between plots. This is achieved by adding set grid before the plot commands. Grids add a lot of visual noise to the plot, so an alternative to the fully x-y grid is to only plot horizontal lines at each of the y-axis tic marks, since we can directly compare x-axis positions without a grid overlay. In this case, you can add a “grid” with only y-axis marks by using set grid ytics, which produces the following output:\nIt’s also very important to ensure that the axis ranges (both x and y) are the same for each plot in the figure, otherwise the reader is likely going to misinterpret your data. The best way to achieve this is to first plot your data separately and figure out the x- and y-ranges which will best fit all the data sets, then do set xrange [\u0026lt;min\u0026gt;:\u0026lt;max\u0026gt;] and set yrange [\u0026lt;min\u0026gt;:\u0026lt;max\u0026gt;] before setting up the multiplot.\nNote that attributes set before the plot commands will apply to all sub-plots; if you only want to set the grid for a subset of the plots then you’ll need to enclose the desired plot commands in a pair of set \u0026lt;thing\u0026gt; and unset \u0026lt;thing\u0026gt;, like so:\nset multiplot 3,1 # Vertical layout with three plots plot sin(x), sin(2*x) set grid # Only draw a grid around the middle plot plot tan(x) unset grid plot cos(x), cos(2*x) This will produce output like the following:\nThis technique can be used with any parameter controlled by the set command (e.g. titles, axis scales) and allows for very fine-grained over the individual plots making up a figure.\nCurve fitting The last topic we’re going to cover in this guide is curve-fitting: finding a function which closely approximates the form of some data. The most common form of this is a linear regression, where we try to fit a straight line of the form f(x) = a*x + b using the method of least-squares. This method is a robust tool in statistical analysis and can be useful when exploring some data (e.g. to see if it has the expected functional form), as well as a way to make quick and easy predictions about hypothetical data outside the set (e.g. extrapolation or interpolation).\nGnuplot requires two things when performing a fit: the functional form of the fit and the source data to be approximated. The source data can come from a text file, and must take the same form as when supplying data for plotting. To define the functional form (i.e. the model we expect the data to fit), you’ll need to define a function with at least one free parameter, which can have whatever name you like. For example, to define a linear fitting function, we would do:\nWe can then use this function with the fit command, which has the following syntax:\nfit f(x) \u0026#34;data.dat\u0026#34; using \u0026lt;cols\u0026gt; via \u0026lt;vars\u0026gt; where \u0026lt;vars\u0026gt; is a comma-separated list of the free parameters which gnuplot will vary to find the best fit. So given our linear function, we would do:\nfit f(x) \u0026#34;data.dat\u0026#34; using 1:2 via a,b Let’s look at a concrete example of performing a linear regression. Consider the following data:\n#data.dat #x y 0.0 -2.273914234802408 0.27 -1.9603314569030421 0.55 -0.2663950721613818 0.83 -0.4126761783258833 1.11 0.9095001376401037 1.38 0.7067620098032337 1.66 1.0439692737424817 1.94 3.248213472626208 2.22 4.887243229020958 2.5 4.456115432892568 Which when plotted looks like this:\nThis data looks sorta linear (indeed it approximately is, as I generated this data by randomly displacing points around the line y = 3\\*x - 2.5), so let’s define a linear fitting function as before and then fit it to the data:\nf(x) = a*x + b fit f(x) \u0026#34;data.dat\u0026#34; via a,b Which prints the following output to the terminal:\niter chisq delta/lim lambda a b 0 4.0253222543e+01 0.00e+00 1.26e+00 1.000000e+00 1.000000e+00 1 1.0018237054e+01 -3.02e+05 1.26e-01 1.951327e+00 -1.073626e+00 2 3.8721663363e+00 -1.59e+05 1.26e-02 2.834953e+00 -2.507549e+00 3 3.8718259096e+00 -8.79e+00 1.26e-03 2.841677e+00 -2.518247e+00 4 3.8718259096e+00 -4.95e-08 1.26e-04 2.841677e+00 -2.518248e+00 iter chisq delta/lim lambda a b After 4 iterations the fit converged. final sum of squares of residuals : 3.87183 rel. change during last iteration : -4.9515e-13 degrees of freedom (FIT_NDF) : 8 rms of residuals (FIT_STDFIT) = sqrt(WSSR/ndf) : 0.695685 variance of residuals (reduced chisquare) = WSSR/ndf : 0.483978 Final set of parameters Asymptotic Standard Error ======================= ========================== a = 2.84168 +/- 0.2757 (9.703%) b = -2.51825 +/- 0.4089 (16.24%) correlation matrix of the fit parameters: a b a 1.000 b -0.843 1.000 This output shows that gnuplot uses an iterative least-squares procedure and prints the fitting parameters (and statistical tests) at each timestep, as well as whether or not the fitting algorithm converged. In this case it did, so gnuplot prints some goodness of fit measures, followed by the fit parameters a and b, which we can see are a = 2.84168 +/- 0.2757 and b = -2.51825 +/- 0.4089. This is consistent with the “true” parameters a = 3 and b = -2.5, although the error is somewhat large due to the small number of points (and potentially the size of the random displacement I applied). Finally, we can plot the fit alongside the data with the following commands:\nset key top left plot \u0026#34;data.dat\u0026#34; title \u0026#34;Raw data\u0026#34;, 2.84168*x - 2.51825 title \u0026#34;Linear fit\u0026#34; Linear fits are super easy and fairly robust, and also give statistical goodness-of-fit measurements which can be extremely useful when analysing your data. The power of gnuplot’s fitting algorithms are even more clear when considering nonlinear fits, which are somewhat tricky to implement and not well supported by many common programs.\nLet’s look at another set of noisy data, this time with a clear periodic structure:\n#periodic.dat #x y -3.14 0.7340049209405408 -2.69 0.9595473520190202 -2.24 0.6220387687186373 -1.79 0.1562411733785349 -1.34 -0.708672547081825 -0.89 -0.9994391197766682 -0.44 -0.6664953617196516 0 0.26607170984999007 0.44 0.9192318773207327 0.89 0.8917499961805632 1.34 0.9708691749801293 1.79 -0.5586074806127096 2.24 -0.9396434545238109 2.69 -0.6098145084879657 3.14 0.9791578865881471 For periodic data like this, a sinusoidal curve is a natural choice of fitting function. Let’s define our function with free parameters for the amplitude, frequency and phase:\nf(x) = amp*sin(freq*x + phase) fit f(x) \u0026#34;periodic.dat\u0026#34; via amp,freq,phase which gives the output:\niter chisq delta/lim lambda amp freq phase 0 1.8014796290e+01 0.00e+00 9.66e-01 1.030402e+00 1.000000e+00 1.000000e+00 1 8.8717970657e+00 -1.03e+05 9.66e-02 4.008161e-02 1.206675e+00 7.219334e-01 * 8.9296657920e+00 6.48e+02 9.66e-01 2.466654e-01 7.530360e+00 -3.825791e+00 2 7.0098731936e+00 -2.66e+04 9.66e-02 2.089800e-01 1.580520e+00 6.074243e-01 * 1.2429866806e+01 4.36e+04 9.66e-01 6.883115e-01 3.076662e+00 -4.618866e-01 3 6.2150602990e+00 -1.28e+04 9.66e-02 6.562097e-01 2.409032e+00 3.096356e-01 4 3.3405311172e+00 -8.61e+04 9.66e-03 6.733933e-01 1.740402e+00 1.449568e-01 5 1.9406758454e+00 -7.21e+04 9.66e-04 9.116653e-01 2.084146e+00 3.787007e-01 6 1.4219845960e+00 -3.65e+04 9.66e-05 1.006998e+00 1.952006e+00 2.991154e-01 7 1.3964835044e+00 -1.83e+03 9.66e-06 1.029539e+00 1.975956e+00 3.227153e-01 8 1.3963752200e+00 -7.75e+00 9.66e-07 1.030381e+00 1.974359e+00 3.212531e-01 9 1.3963747219e+00 -3.57e-02 9.66e-08 1.030403e+00 1.974441e+00 3.213342e-01 iter chisq delta/lim lambda amp freq phase After 9 iterations the fit converged. final sum of squares of residuals : 1.39637 rel. change during last iteration : -3.56728e-07 degrees of freedom (FIT_NDF) : 12 rms of residuals (FIT_STDFIT) = sqrt(WSSR/ndf) : 0.341123 variance of residuals (reduced chisquare) = WSSR/ndf : 0.116365 Final set of parameters Asymptotic Standard Error ======================= ========================== amp = 1.0304 +/- 0.128 (12.43%) freq = 1.97444 +/- 0.05834 (2.955%) phase = 0.321334 +/- 0.1185 (36.88%) correlation matrix of the fit parameters: amp freq phase amp 1.000 freq 0.085 1.000 phase -0.039 -0.061 1.000 We can see that the nonlinear fit takes slightly longer to converge than the linear example (although it’s still relatively quick), and gives values for the fit parameters to an “okay” degree of accuracy (~10% - 30%, again likely due to the quality of the data). The fitted parameters are also consistent with the “true” values I used to generate the data (amp = 1, freq = 2, phase = 0.25), and visually fits the data fairly well:\nThis example should hopefully serve to demonstrate that gnuplot makes fitting linear and nonlinear functions to data no more difficult than plotting that same data. It’s not quite a “one-click” process, as some care is required when selecting the fitting function: using too many or too few free parameters can slow down or hamper convergence, as can choosing the wrong functional form of the fit. This process works best when you have some a priori guess as to the data’s functional form (e.g. based on the physics underlying the data set), and you may need to choose different weighting for data points via the errors keyword to fit. If you find yourself needing to do curve-fitting, it’s worth having a read over this guide first for an idea of best-practices and pitfalls.\nFurther reading This guide is intended to be a sort of “crash-course” introduction to gnuplot, so there are several important topics in data visualisation which we haven’t touched on. In particular, I’ve deliberately left out 3D visualisations as they can be extremely tricky to get right and are a large enough topic to warrant their own guide. I have tried to include general visualisation best-practices throughout the guide, but the topic of visualisation is vast and this guide is meant to be short. To that end, here are some useful resources you might like to familiarise yourself with in order to get the most out of your plots and figures:\nClaus Wilke, Fundamentals of Data Visualization, (2019) O’Reilly Media. URL: https://clauswilke.com/dataviz/ This book does not focus on a single plotting program, instead aiming to be a general guide to making good plots which efficiently convey the desired information. It contains a number of real examples and covers a wide-range of visualisation techniques beyond those covered in this guide. It is freely available on the author’s website under a Creative Commons license. Phillip Janert, Gnuplot in Action: Understanding Data with Graphs, (2009) Manning Publications. URL: https://www.manning.com/books/gnuplot-in-action This book serves as both a reference manual for gnuplot, as well as a “cookbook” containing a lot of example scripts you can modify to suit your purposes. The book is not freely available, but the UQ Library has a physical copy in stock (as of May 2021). National Institute of Standards and Technology (NIST), eHandbook of Statistical Methods, (2012). URL: https://www.itl.nist.gov/div898/handbook/index.htm Handbook of methods and best-practices for statistics and data analysis in engineering. Of particular interest to this guide is “Chapter 1: Exploratory Data Analysis”, which includes procedures and discussions of analysis techniques (both graphical and non-graphical) to uncover the underlying structure, distribution and important features of a data set. Harvey Motulsky \u0026amp; Arthur Christopoulos, Fitting Models to Biological Data using Linear and Nonlinear Regression, (2003) GraphPad Software, Inc. URL: https://www.facm.ucl.ac.be/cooperation/Vietnam/WBI-Vietnam-October-2011/Modelling/RegressionBook.pdf Guide to curve fitting and data interpolation. Even though the examples are bioscience-themed, the book still provides a lot of best-practices and pitfalls to avoid when fitting a model to your data. ","permalink":"https://geowisdom.com.cn/posts/skills/visualising-and-plotting-data-with-gnuplot/","summary":"\u003ch2 id=\"introduction\"\u003eIntroduction\u003c/h2\u003e\n\u003cp\u003eData visualisation is extremely important for communicating the results of your research, either in a journal or to the general public, and for analysing and learning more about the characteristics of your data and system (so-called “exploratory data analysis”). One of the most fundamental tools in data visualisation is the two-dimensional plot (or graph). This tutorial will cover the basics of two-dimensional data visualisation using a program called \u003ccode\u003egnuplot\u003c/code\u003e; a program which allows you to create high-quality, visually-pleasing figures and undertake robust post-hoc data analysis.\u003c/p\u003e","title":"Visualising and plotting data with gnuplot"},{"content":"PLY 格式是在 1990 年代由 Greg Turk 和斯坦福图形实验室的其他人开发的，这也是它被称为斯坦福三角格式的原因。自那时起，该文件格式一直保持 1.0 版本，没有进一步的修改。\n文件格式 一个简单的 PLY 对象由一组用于表示对象的元素组成。它包含一个顶点列表（由(x,y,z)三元组组成）和一个面列表（实际上是顶点列表的索引）。顶点和面是两种元素的例子，而大多数 PLY 文件主要由这两种元素构成。此外，还可以创建新的属性并将其附加到对象的元素上，这些但属性应以一种不会破坏旧程序的方式添加，以便旧程序在遇到这些新属性时仍能正常运行。读取应用程序也可以忽略这些属性。此外，还可以创建新的元素，并为这些元素定义属性。\nPLY 文件格式的文件结构如下：\n文件头 顶点列表 面列表 其他元素的列表 示例结构\n在后续讨论中，我们将使用以下示例来说明 PLY 文件格式的各个部分。\nply format ascii 1.0 { ascii/binary，格式版本号 } comment made by Greg Turk { 以 comment 关键字开头的注释 } comment this file is a cube element vertex 8 { 定义“顶点”元素，文件中有 8 个 } property float x { 顶点包含浮点数“x”坐标 } property float y { y 坐标也是顶点的属性 } property float z { z 坐标也是 } element face 6 { 文件中有 6 个“面”元素 } property list uchar int vertex_index { “vertex_indices” 是一个整数列表 } end_header { 标记头部的结束 } 0 0 0 { 顶点列表的开始 } 0 0 1 0 1 1 0 1 0 1 0 0 1 0 1 1 1 1 1 1 0 4 0 1 2 3 { 面列表的开始 } 4 7 6 5 4 4 0 4 5 1 4 1 5 6 2 4 2 6 7 3 4 3 7 4 0 文件头 PLY 文件格式的文件头由 ASCII 文本组成，无论是 ASCII 格式还是二进制格式都是如此。头部部分的开始和结束由ply和end_header关键字标识。头部的开始有一个魔术词ply，用于让读取器识别 PLY 文件格式。下一行显示了该文件的版本号。PLY 文件格式中的注释以comment关键字开头。\n元素关键字 element关键字用于说明文件中包含的内容。它后面是该特定元素类型的属性，每个属性都有其类型和顺序，如下所示：\nelement vertex 8 { 定义“顶点”元素，文件中有 8 个 } property float x { 顶包含点浮点数“x”坐标 } property float y { y 坐标也是顶点的属性 } property float z { z 坐标也是 } 在这个特定的例子中，顶点元素有 3 个属性，类型为浮点数，顺序已指定。\n数据类型 属性可能有两种数据类型： 标量：标量数据类型如下表所示：\n名称 类型 字节数 char 字符 1 uchar 无符号字符 1 short 短整数 2 ushort 无符号短整数 2 int 整数 4 uint 无符号整数 4 float 单精度浮点数 4 double 双精度浮点数 8 列表：还有一种特殊的属性定义形式，使用列表数据类型。例如，上面立方体文件中的： property list uchar int vertex_index 这意味着属性vertex_index包含一个无符号字符，表示该属性包含的索引数量，后面跟着一个整数列表，列表长度等于该数量。这个可变长度列表中的每个整数都是一个顶点的索引。\n例子文件 下图是一个ply格式保存的块体模型例子，顶点附有颜色属性。macOS系统可以使用预览程序直接查看，其他系统可以使用paraview或者meshlab等软件查看。\nply格式块体模型（附带顶点颜色） 点击打开顶点数据模型 和 快体数据模型。\n程序读写 C++程序读写 ","permalink":"https://geowisdom.com.cn/posts/skills/ply%E6%96%87%E4%BB%B6%E8%AF%BB%E5%86%99/","summary":"\u003cp\u003ePLY 格式是在 1990 年代由 Greg Turk 和斯坦福图形实验室的其他人开发的，这也是它被称为斯坦福三角格式的原因。自那时起，该文件格式一直保持 1.0 版本，没有进一步的修改。\u003c/p\u003e\n\u003ch3 id=\"文件格式\"\u003e文件格式\u003c/h3\u003e\n\u003cp\u003e一个简单的 PLY 对象由一组用于表示对象的元素组成。它包含一个顶点列表（由(x,y,z)三元组组成）和一个面列表（实际上是顶点列表的索引）。顶点和面是两种元素的例子，而大多数 PLY 文件主要由这两种元素构成。此外，还可以创建新的属性并将其附加到对象的元素上，这些但属性应以一种不会破坏旧程序的方式添加，以便旧程序在遇到这些新属性时仍能正常运行。读取应用程序也可以忽略这些属性。此外，还可以创建新的元素，并为这些元素定义属性。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ePLY 文件格式的文件结构如下：\u003c/strong\u003e\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e文件头\u003c/li\u003e\n\u003cli\u003e顶点列表\u003c/li\u003e\n\u003cli\u003e面列表\u003c/li\u003e\n\u003cli\u003e其他元素的列表\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e\u003cstrong\u003e示例结构\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e在后续讨论中，我们将使用以下示例来说明 PLY 文件格式的各个部分。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-plaintext\" data-lang=\"plaintext\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eply  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eformat ascii 1.0           { ascii/binary，格式版本号 }  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecomment made by Greg Turk  { 以 comment 关键字开头的注释 }  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecomment this file is a cube  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eelement vertex 8           { 定义“顶点”元素，文件中有 8 个 }  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eproperty float x           { 顶点包含浮点数“x”坐标 }  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eproperty float y           { y 坐标也是顶点的属性 }  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eproperty float z           { z 坐标也是 }  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eelement face 6             { 文件中有 6 个“面”元素 }  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eproperty list uchar int vertex_index { “vertex_indices” 是一个整数列表 }  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eend_header                 { 标记头部的结束 }  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e0 0 0                      { 顶点列表的开始 }  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e0 0 1  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e0 1 1  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e0 1 0  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e1 0 0  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e1 0 1  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e1 1 1  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e1 1 0  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e4 0 1 2 3                  { 面列表的开始 }  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e4 7 6 5 4  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e4 0 4 5 1  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e4 1 5 6 2  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e4 2 6 7 3  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e4 3 7 4 0  \n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch4 id=\"文件头\"\u003e文件头\u003c/h4\u003e\n\u003cp\u003ePLY 文件格式的文件头由 ASCII 文本组成，无论是 ASCII 格式还是二进制格式都是如此。头部部分的开始和结束由\u003ccode\u003eply\u003c/code\u003e和\u003ccode\u003eend_header\u003c/code\u003e关键字标识。头部的开始有一个魔术词\u003ccode\u003eply\u003c/code\u003e，用于让读取器识别 PLY 文件格式。下一行显示了该文件的版本号。PLY 文件格式中的注释以\u003ccode\u003ecomment\u003c/code\u003e关键字开头。\u003c/p\u003e","title":"PLY格式介绍"}]