gctl/lib/io/dsv_io.h
2025-01-02 09:40:27 +08:00

830 lines
21 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/********************************************************
* ██████╗ ██████╗████████╗██╗
* ██╔════╝ ██╔════╝╚══██╔══╝██║
* ██║ ███╗██║ ██║ ██║
* ██║ ██║██║ ██║ ██║
* ╚██████╔╝╚██████╗ ██║ ███████╗
* ╚═════╝ ╚═════╝ ╚═╝ ╚══════╝
* Geophysical Computational Tools & Library (GCTL)
*
* Copyright (c) 2023 Yi Zhang (yizhang-geo@zju.edu.cn)
*
* GCTL is distributed under a dual licensing scheme. You can redistribute
* it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 2
* of the License, or (at your option) any later version. You should have
* received a copy of the GNU Lesser General Public License along with this
* program. If not, see <http://www.gnu.org/licenses/>.
*
* If the terms and conditions of the LGPL v.2. would prevent you from using
* the GCTL, please consider the option to obtain a commercial license for a
* fee. These licenses are offered by the GCTL's original author. As a rule,
* licenses are provided "as-is", unlimited in time for a one time fee. Please
* send corresponding requests to: yizhang-geo@zju.edu.cn. Please do not forget
* to include some description of your company and the realm of its activities.
* Also add information on how to contact you by electronic and paper mail.
******************************************************/
#ifndef _GCTL_DSV_IO_H
#define _GCTL_DSV_IO_H
#include "../core.h"
#include "../utility.h"
#include "../geometry.h"
#include "regex.h"
namespace gctl
{
enum cell_type_e
{
String,
Int,
Float,
};
struct table_cell
{
std::string str_; // 单元格的内容 统一保存为字符串
cell_type_e type_; // 类型字符串
bool out_ok_; // 是否可输出到文件
table_cell()
{
str_ = "";
type_ = String;
out_ok_ = true;
}
template <typename T> T value()
{
// 如果输出也是字符串 就直接赋值即可避免l_str中含有空格会出现bug
if constexpr (std::is_same<T, std::string>::value) return str_;
else
{
T out;
str2type(str_, out);
return out;
}
}
template <typename T> void value(const T &in, int p = 6)
{
// 单元被赋值时使用的类型
const std::type_info &tinfo = typeid(T);
std::smatch ret;
std::regex pat("basic_string");
std::string t_str = tinfo.name();
if (regex_search(t_str, ret, pat)) type_ = String;
else if (t_str == "i") type_ = Int;
else if (t_str == "f") type_ = Float;
else if (t_str == "d") type_ = Float;
// 对于double类型 可以设置转换的有效数字位数(精度)
if constexpr (std::is_same<T, double>::value)
{
std::stringstream ss;
ss.precision(p);
ss << in;
ss >> str_;
}
else if constexpr (std::is_same<T, std::string>::value) str_ = in;
else str_ = std::to_string(in);
return;
}
};
/**
* @brief 表格的头信息类型
*
*/
enum table_headtype_e
{
NoHead, // 没有表头
BothHead, // 同时有行与列表头
ColumnHead, // 只有列表头
RowHead, // 只有行表头
};
/**
* @brief DSV文本读写类
*
* 可以处理的文本数据应该符合下述要求:
* 1. 以'#'开始的行均为注释行,标识符可由用户指定;
* 2. 以'#!'开始的行均为标记行,标识符可由用户指定;
* 3. 文本开始可以包含n行头信息
* 4. 数据体为一个row*col大小的表格
* 5. 数据表格文件可以额外包含一列行名称与列名称;
* 6. 如论文件内是否包含行头或列头均可使用内置的行名称R<id>或列名称C<id>指定相应的行或列
*/
class dsv_io
{
protected:
// 文件名
std::string file_;
// 头信息行数 表格行数(不包括表头) 表格列数(不包括表头)
int head_num_, row_num_, col_num_;
// 注释行起始符 标记行起始符 分割符
char att_sym_, tag_sym_, deli_sym_;
// 头信息行 注释行 标记行
std::vector<std::string> heads_, annotates_, tags_;
// 内容表格大小为row_num_+1 * col_num_+1
std::vector<std::vector<table_cell> > table_;
public:
/**
* @brief Construct a new text content object
*
*/
dsv_io();
/**
* @brief Destroy the text content object
*
*/
~dsv_io();
/**
* @brief Construct a new text descriptor object and load text file
*
* @param filename 文件名
* @param file_exten 文件扩展名
* @param t 表格是否有行和列名称
*/
dsv_io(std::string filename, std::string file_exten = ".txt", table_headtype_e t = NoHead);
/**
* @brief 清理字符串向量对象
*
*/
void clear();
/**
* @brief 返回头信息行数
*
* @return 行数
*/
int head_number(){return head_num_;}
/**
* @brief 返回数据行数
*
* @return 行数
*/
int row_number(){return row_num_;}
/**
* @brief 返回数据列数
*
* @return 列数
*/
int col_number(){return col_num_;}
/**
* @brief 返回头信息
*
* @return 头信息
*/
const std::vector<std::string>& get_head_records(){return heads_;}
/**
* @brief 返回注释行
*
* @return 注释行
*/
const std::vector<std::string>& get_annotoations(){return annotates_;}
/**
* @brief 返回标记行
*
* @return 标记
*/
const std::vector<std::string>& get_tags(){return tags_;}
/**
* @brief 获取行名称
*
* @param names 名称
*/
void get_row_names(std::vector<std::string> &names);
/**
* @brief 获取列名称
*
* @param names 名称
*/
void get_column_names(std::vector<std::string> &names);
/**
* @brief 设置列分隔符
*
* @param deli_sym 分隔符
*/
void set_delimeter(char deli_sym){deli_sym_ = deli_sym;}
/**
* @brief 设置头信息行数
*
* @param num 行数
*/
void set_head_number(char num){head_num_ = num;}
/**
* @brief 设置注释行符号
*
* @param att_sym 注释符号
*/
void set_annotation_symbol(char att_sym){att_sym_ = att_sym;}
/**
* @brief 设置标记行符号
*
* @param tag_sym 标记符号
*/
void set_tag_symbol(char tag_sym){tag_sym_ = tag_sym;}
/**
* @brief 设置头信息
*
* @param heads 头信息
*/
void set_head_records(const std::vector<std::string> &heads){heads_ = heads; head_num_ = heads_.size();}
/**
* @brief 设置注释
*
* @param att 注释
*/
void set_annotoations(const std::vector<std::string> &att){annotates_ = att;}
/**
* @brief 设置标记
*
* @param tags 标记
*/
void set_tags(const std::vector<std::string> &tags){tags_ = tags;}
/**
* @brief 设置行名称
*
* @param names 名称
*/
void set_row_names(const std::vector<std::string> &names);
/**
* @brief 设置列名称
*
* @param names 名称
*/
void set_column_names(const std::vector<std::string> &names);
/**
* @brief 设置行类型
*
* @param t 类型名称 String|Float|Int
* @param idx 行索引
*/
void set_row_type(cell_type_e t, int idx);
/**
* @brief 设置行类型
*
* @param t 类型名称 String|Float|Int
* @param name 行名称
*/
void set_row_type(cell_type_e t, std::string name);
/**
* @brief 设置列类型
*
* @param t 类型名称 String|Float|Int
* @param idx 列索引
*/
void set_column_type(cell_type_e t, int idx);
/**
* @brief 设置列类型
*
* @param t 类型名称 String|Float|Int
* @param name 列名称
*/
void set_column_type(cell_type_e t, std::string name);
/**
* @brief 读入文本文件
*
* @param filename 文件名
* @param file_exten 文件扩展名
* @param t 表格是否有行和列名称
*/
void load_text(std::string filename, std::string file_exten = ".txt", table_headtype_e t = NoHead);
/**
* @brief 读入CSV文件
*
* @param filename 文件名
*/
void load_csv(std::string filename, table_headtype_e t = ColumnHead);
/**
* @brief 将内容写入文件
*
* @param filename 文件名
* @param file_exten 文件扩展名
*/
void save_text(std::string filename, std::string file_exten = ".txt");
/**
* @brief 将内容写入CSV文件
*
* @param filename 文件名(无后缀)
*/
void save_csv(std::string filename);
/**
* @brief 初始化表格
*
* @param row 数据行数
* @param col 数据列数
*/
void init_table(int row, int col, table_headtype_e t = ColumnHead);
/**
* @brief 返回表格信息
*
* @param t 显示表格信息的类型
*/
void info(table_headtype_e t = ColumnHead);
/**
* @brief 返回名称为name和R<id>和C<id>的行或列的索引
*
* @param name 名称 可以是具体的名称如有或者是R<id>和C<id>
* @param iter_row 搜索行名称默认为搜索列名称如果name参数为R<id>和C<id>则此参数无效
* @return 索引 返回的索引大于等于1 小于等于行数或列数)失败则返回-1
*/
int name_index(std::string name, bool iter_row = false);
/**
* @brief 设置列输出。你仍然可以使用这些数据,它们只是不会被输出
*
* @param idx 列索引 从1开始
* @param s 设置输出类型
*/
void column_output(int idx, switch_type_e s = Disable);
/**
* @brief 设置列输出。你仍然可以使用这些数据,它们只是不会被输出
*
* @param name 列名称
* @param s 设置输出类型
*/
void column_output(std::string name, switch_type_e s = Disable);
/**
* @brief 设置行输出。你仍然可以使用这些数据,它们只是不会被输出
*
* @param idx 行索引 从1开始
* @param s 设置输出类型
*/
void row_output(int idx, switch_type_e s = Disable);
/**
* @brief 设置行输出。你仍然可以使用这些数据,它们只是不会被输出
*
* @param name 行名称
* @param s 设置输出类型
*/
void row_output(std::string name, switch_type_e s = Disable);
/**
* @brief 在表格末尾添加一个空白列
*
* @param name 设置列名称
*/
void add_column(std::string name = "");
/**
* @brief 在索引为idx的列插入一个空白列
*
* @param idx 列索引 小于等于0时将在表尾添加一列
* @param name 设置列名称
*/
void add_column(int idx, std::string name = "");
/**
* @brief 在名称为id_name的列插入一个空白列
*
* @param id_name 索引列名称
* @param name 设置列名称
*/
void add_column(std::string id_name, std::string name = "");
/**
* @brief 在表格末尾添加一个空白行
*
* @param name 设置行名称
*/
void add_row(std::string name = "");
/**
* @brief 在索引为idx的列插入一个空白行
*
* @param idx 行索引 小于等于0时将在表尾添加一行
* @param name 设置行名称
*/
void add_row(int idx, std::string name = "");
/**
* @brief 在名称为id_name的列插入一个空白行
*
* @param id_name 索引行名称
* @param name 设置行名称
*/
void add_row(std::string id_name, std::string name = "");
/**
* @brief 填充表格
*
* @tparam T 数据类型
* @param data 矩阵数据 大小与表格一致
* @param p 浮点类数据保存时的有效数字位数
*/
template <typename T> void fill_table(const matrix<T> &data, int p = 6);
/**
* @brief 获取表格
*
* @tparam T 数据类型
* @param data 矩阵数据
*/
template <typename T> void get_table(matrix<T> &data);
/**
* @brief 填充列
*
* @tparam T 数据类型
* @param idx 列索引 从1开始
* @param data 列数据
* @param p 浮点类数据保存时的有效数字位数
*/
template <typename T> void fill_column(const array<T> &data, int idx, int p = 6);
/**
* @brief 填充列
*
* @tparam T 数据类型
* @param name 列名称
* @param data 列数据
* @param p 浮点类数据保存时的有效数字位数
*/
template <typename T> void fill_column(const array<T> &data, std::string name, int p = 6);
/**
* @brief 填充行
*
* @tparam T 数据类型
* @param idx 行索引 从1开始
* @param data 行数据
* @param p 浮点类数据保存时的有效数字位数
*/
template <typename T> void fill_row(const array<T> &data, int idx, int p = 6);
/**
* @brief 填充行
*
* @tparam T 数据类型
* @param name 行名称
* @param data 行数据
* @param p 浮点类数据保存时的有效数字位数
*/
template <typename T> void fill_row(const array<T> &data, std::string name, int p = 6);
/**
* @brief 获取列数据
*
* @tparam T 数据类型
* @param idx 列索引 从1开始
* @param data 列数据
*/
template <typename T> void get_column(array<T> &data, int idx);
/**
* @brief 获取列数据
*
* @tparam T 数据类型
* @param name 列名称
* @param data 列数据
*/
template <typename T> void get_column(array<T> &data, std::string name);
/**
* @brief 获取行数据
*
* @tparam T 数据类型
* @param idx 行索引 从1开始
* @param data 行数据
*/
template <typename T> void get_row(array<T> &data, int idx);
/**
* @brief 获取行数据
*
* @tparam T 数据类型
* @param name 行名称
* @param data 行数据
*/
template <typename T> void get_row(array<T> &data, std::string name);
/**
* @brief 获取表格单元数据
*
* @tparam T 数据类型
* @param r 行号 从0开始可以操作行或列名称
* @param c 列号 从0开始可以操作行或列名称
* @return T 单元数据
*/
template <typename T> T cell(int r, int c){return table_[r][c].value<T>();}
/**
* @brief 填充表格单元数据
*
* @tparam T 数据类型
* @param r 行号 从0开始可以操作行或列名称
* @param c 列号 从0开始可以操作行或列名称
* @param d 数据
* @param p 浮点类数据保存时的有效数字位数
*/
template <typename T> void cell(T d, int r, int c, int p = 6){table_[r][c].value(d, p); return;}
};
template <typename T>
void dsv_io::fill_table(const matrix<T> &data, int p)
{
for (size_t i = 1; i <= std::min(row_num_, (int) data.row_size()); i++)
{
for (size_t j = 1; j <= std::min(col_num_, (int) data.col_size()); j++)
{
table_[i][j].value(data[i - 1][j - 1], p);
}
}
return;
}
template <typename T>
void dsv_io::get_table(matrix<T> &data)
{
data.resize(row_num_, col_num_);
for (size_t i = 1; i <= row_num_; i++)
{
for (size_t j = 1; j <= col_num_; j++)
{
data[i -1][j - 1] = table_[i][j].value<T>();
}
}
return;
}
template <typename T>
void dsv_io::fill_column(const array<T> &data, int idx, int p)
{
if (idx > col_num_ || idx <= 0)
{
throw std::runtime_error("[gctl::dsv_io] Invalid column index.");
}
for (size_t i = 1; i <= std::min(row_num_, (int) data.size()); i++)
{
table_[i][idx].value(data[i - 1], p);
}
return;
}
template <typename T>
void dsv_io::fill_column(const array<T> &data, std::string name, int p)
{
fill_column(data, name_index(name, false), p);
return;
}
template <typename T>
void dsv_io::fill_row(const array<T> &data, int idx, int p)
{
if (idx > row_num_ || idx <= 0)
{
throw std::runtime_error("[gctl::dsv_io] Invalid row index.");
}
for (size_t i = 1; i <= std::min(col_num_, (int) data.size()); i++)
{
table_[idx][i].value(data[i - 1], p);
}
return;
}
template <typename T>
void dsv_io::fill_row(const array<T> &data, std::string name, int p)
{
fill_row(data, name_index(name, true), p);
return;
}
template <typename T>
void dsv_io::get_column(array<T> &data, int idx)
{
if (idx > col_num_ || idx <= 0)
{
throw std::runtime_error("[gctl::dsv_io] Invalid column index.");
}
data.resize(row_num_);
for (size_t i = 1; i <= row_num_; i++)
{
data[i - 1] = table_[i][idx].value<T>();
}
return;
}
template <typename T>
void dsv_io::get_column(array<T> &data, std::string name)
{
get_column(data, name_index(name, false));
return;
}
template <typename T>
void dsv_io::get_row(array<T> &data, int idx)
{
if (idx > row_num_ || idx <= 0)
{
throw std::runtime_error("[gctl::dsv_io] Invalid row index.");
}
data.resize(col_num_);
for (size_t i = 1; i <= col_num_; i++)
{
data[i - 1] = table_[idx][i].value<T>();
}
return;
}
template <typename T>
void dsv_io::get_row(array<T> &data, std::string name)
{
get_row(data, name_index(name, true));
return;
}
/**
* @brief 地理数据类型DSV文件读写类
*
*/
class geodsv_io : public dsv_io
{
public:
/**
* @brief Construct a new geodsv_io object
*
*/
geodsv_io();
/**
* @brief Destroy the geodsv_io object
*
*/
~geodsv_io();
/**
* @brief Construct a new text descriptor object and load text file
*
* @param filename 文件名
* @param file_exten 文件扩展名
*/
geodsv_io(std::string filename, std::string file_exten = ".txt", table_headtype_e t = NoHead);
/**
* @brief 填充二维坐标列
*
* @param xid x坐标列索引 从1开始
* @param yid y坐标列索引 从1开始
* @param data 返回的二维坐标数据
* @param p 填入的浮点数据有效位数(精度)
*/
void fill_column_point2dc(const array<point2dc> &data, int xid, int yid, int p = 6);
/**
* @brief 填充二维坐标列
*
* @param xname x坐标列名称
* @param yname y坐标列名称
* @param data 返回的二维坐标数据
* @param p 填入的浮点数据有效位数(精度)
*/
void fill_column_point2dc(const array<point2dc> &data, std::string xname, std::string yname, int p = 6);
/**
* @brief 填充三维坐标列
*
* @param xid x坐标列索引 从1开始
* @param yid y坐标列索引 从1开始
* @param zid z坐标列索引 从1开始
* @param data 返回的三维坐标数据
* @param p 填入的浮点数据有效位数(精度)
*/
void fill_column_point3dc(const array<point3dc> &data, int xid, int yid, int zid, int p = 6);
/**
* @brief 填充三维坐标列
*
* @param xname x坐标列名称
* @param yname y坐标列名称
* @param zname z坐标列名称
* @param data 返回的三维坐标数据
* @param p 填入的浮点数据有效位数(精度)
*/
void fill_column_point3dc(const array<point3dc> &data, std::string xname, std::string yname, std::string zname, int p = 6);
/**
* @brief 填充三维坐标列
*
* @param rid rad坐标列索引 从1开始
* @param pid phi坐标经度列索引 从1开始
* @param tid theta坐标纬度列索引 从1开始
* @param data 返回的三维坐标数据
* @param p 填入的浮点数据有效位数(精度)
*/
void fill_column_point3ds(const array<point3ds> &data, int rid, int pid, int tid, int p = 6);
/**
* @brief 填充三维坐标列
*
* @param rname rad坐标列名称 从1开始
* @param pname phi坐标经度列名称 从1开始
* @param tname theta坐标纬度列名称 从1开始
* @param data 返回的三维坐标数据
* @param p 填入的浮点数据有效位数(精度)
*/
void fill_column_point3ds(const array<point3ds> &data, std::string rname, std::string pname, std::string tname, int p = 6);
/**
* @brief 读取二维坐标列
*
* @param xid x坐标列索引 从1开始
* @param yid y坐标列索引 从1开始
* @param data 返回的二维坐标数据
*/
void get_column_point2dc(array<point2dc> &data, int xid, int yid);
/**
* @brief 读取二维坐标列
*
* @param xname x坐标列名称
* @param yname y坐标列名称
* @param data 返回的二维坐标数据
*/
void get_column_point2dc(array<point2dc> &data, std::string xname, std::string yname);
/**
* @brief 读取三维坐标列
*
* @param xid x坐标列索引 从1开始
* @param yid y坐标列索引 从1开始
* @param zid z坐标列索引 从1开始
* @param data 返回的三维坐标数据
*/
void get_column_point3dc(array<point3dc> &data, int xid, int yid, int zid);
/**
* @brief 读取三维坐标列
*
* @param xname x坐标列名称
* @param yname y坐标列名称
* @param zname z坐标列名称
* @param data 返回的三维坐标数据
*/
void get_column_point3dc(array<point3dc> &data, std::string xname, std::string yname, std::string zname);
/**
* @brief 读取三维坐标列
*
* @param rid rad坐标列索引 从1开始
* @param pid phi坐标经度列索引 从1开始
* @param tid theta坐标纬度列索引 从1开始
* @param data 返回的三维坐标数据
*/
void get_column_point3ds(array<point3ds> &data, int rid, int pid, int tid);
/**
* @brief 读取三维坐标列
*
* @param rname rad坐标列名称 从1开始
* @param pname phi坐标经度列名称 从1开始
* @param tname theta坐标纬度列名称 从1开始
* @param data 返回的三维坐标数据
*/
void get_column_point3ds(array<point3ds> &data, std::string rname, std::string pname, std::string tname);
};
}
#endif //_GCTL_DSV_IO_H