公式代码化之——二维表格数据C语言快速定位与读取方法分享

文章介绍了在C语言中处理二维表格数据的挑战,提出了两种数据筛选方法:最近点优先原则和加权平均原则。通过实现GetNearestElementPosition、ReadLine和Extract等函数,实现了从txt文件中读取和处理数据。最后,通过FindNumber函数整合整个流程,根据给定参数从表格中查找相应数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在使用C语言转化公式进行计算时,易发现读取二维表格是一个较为困难的问题。尽管查找了相关资料,但未发现特定的解决方法。因此,作者自行开发了一套代码,以实现对二维表格数据的检索。

二维表格

编写公式时,我们可能需要使用一些经验数据来计算参数值,这就需要我们查表以获取相应的数值。例如某个公式中涉及的参数C,其值由角度θ和lg(A)决定,其中θ和面积A是已知量。

表1 C的经验公式

使用表格选取数据时,我们需要输入的两个量θ和lg(A),查表得到参数C的值。首先需要确定一种估算方法,因为实际情况下给出的角度不一定是整数,比如θ可能为32.15°,lg(A)可能为1.496。

由于在实际工况下给出的参数通常并不能精准匹配到表格中给出的参数值,因此在查表时需要对数据进行筛选和处理这里有两种较为常用的方法:

1.最近点优先原则采用欧氏距离度量给出的参数点与表格中给出的参数点之间的距离,选取与给出参数点最近点的数据点作为最终结果 ,例如给定的角度θ=32.15°处于30°和40°中间,相对更靠近30°,lg(A)=1.496处于lg(A)=1.4和lg(A)=1.5之间,相对更靠近1.5,根据距离的大小估计C的值最接近4.6,那么C=4.6,如下表所示,黄色区域的值。

2.加权平均原则: 根据欧氏距离筛选出与给出数据点最近的四个数据点,采用加权平均方法得到最终输出。权重的确定可采用距离的反比来确定。如下图所示,对黄色区域的四个数据进行加权平均。

由于上述两种方法为较为简单常用的数据筛选方法,实际情况下需要根据具体表数据类型来确定数据筛选与处理策略,在本文的情况下中直接采用最近点优先原则即可。

数据存储

解决估算的问题,就需要考虑以何种格式存储数据。当然如果处理表格较少,可以将数据写成容器或者其他形式存储在代码内,但是考虑到后期维护的方便性,建议使用Excel表格,或者使用txt文档存储数据,如果后期有对经验公式的数据进行修改,可以直接修改文档。本文使用txt文档存储数据,存储效果如下图所示。

数字之间用空格符号分隔,只存储了C的数据,并将txt文档命名为TableC。

代码实现步骤

确定了数据的估算方法和存储方法,接下来就需要考虑如何根据已知信息找到我们需要的数值,总共分为以下三步:

➤ 1、根据输入的角度θ、lg(A)、横轴数组和纵轴数组进行判断需要读取哪一行那一列的数据,输出读取数据所在行和所在列,使用的函数为GetNearestElementPosition()。

➤ 2、根据输入的txt文件路径,和所需数据所在行,输出txt文件中对应行的字符串,使用的函数为ReadLine()。

➤ 3、根据输入的字符串和所需数据所在列,输出对应列的数据,使用的函数为Extract()。

判断数据所在行列

首先需要根据输入的数据判断出需要读取的行和列。函数GetNearest()可以根据输入的目标数判断距离较近的值。函数GetNearestElementPosition()的作用是判断数组中与目标值差距最小的数的位置。

#include<iostream>  #include<fstream>  #include<math.h>  #include <sstream>    using namespace std;   /* * @brief  判断目标数与传入参数距离 * @input  与目标数进行距离判断的两个数、目标值 * @return 距离目标数较近的数 * @author YuHaoHao(1813481317@qq.com) * @date   2023-04-22 */  double GetNearest(double x, double y, double target) {      if (target - x >= y - target)          return y;      else          return x;  }    /* * @brief  寻找数组中与目标值差值最小的数的位置 * @input  数组、数组元素个数、目标值 * @return 数组中与目标数差值最小的数的位置 * @author YuHaoHao(1813481317@qq.com) * @date   2023-04-22 */  int GetNearestElementPosition(double arr[], int n, double target) {      double result = 0;      if (target <= arr[0])      {          result = arr[0];      }      if (target >= arr[n - 1])      {          result = arr[n - 1];      }      int left = 0, right = n, mid = 0;      while (left < right) {          mid = (left + right) / 2;          if (abs(arr[mid] - target) < 1e-8)          {              result = arr[mid];          }          if (target < arr[mid])          {              if (mid > 0 && target > arr[mid - 1])              {                  result = GetNearest(arr[mid - 1], arr[mid], target);              }              right = mid;          }          else          {              if (mid < n - 1 && target < arr[mid + 1])              {                  result = GetNearest(arr[mid], arr[mid + 1], target);              }              left = mid + 1;          }      }      int num = 0;      while (result != arr[num])      {          num++;      }      return num + 1;  }  

检验一下是否可以正确地进行估算,测试函数如下,数组arry[]是转角θ的数据,数组包含的元素为10、20、30、40、50、60。假设输入的目标值为32.15°,那么根据最近判断,应该读取第三行的数据,上面GetNearestElementPosition函数的返回值因为3,测试代码如下所示。

int main()  {      double arry[6] = { 10,20,30,40,50,60 };      cout << "读取第" << GetNearestElementPosition(arry, 6, 32.15)                << "行。" << endl;    system("pause");      return 0;  }

控制台输出结果如下:

根据输入的目标数就可以得到需要读取第几行第几列的数据。接下来需要根据需要打开txt文件读取数据。

读取指定行的数据

首先提取所需行的数据并以字符串的形式存储一行的内容,下面的函数CountLines()是用于计算文档中总共有多少行数据,ReadLine()是读取文件中指定行的数据。

/* * @brief  判断文件中数据的行数 * @input  文件地址 * @return 文件中数据的行数 * @author YuHaoHao(1813481317@qq.com) * @date   2023-04-22 */  int CountLines(string filename)  {      ifstream ReadFile;      int n = 0;      string tmp;      ReadFile.open(filename.c_str());      if (ReadFile.fail())      {          return 0;      }      else      {          while (getline(ReadFile, tmp, '\n'))          {              n++;          }          ReadFile.close();          return n;      }  }      /* * @brief  读取文件中所需行的数据 * @input  文件地址、所需数据在第几行 * @return string类型,文件第line行的字符串 * @author YuHaoHao(1813481317@qq.com) * @date   2023-04-22 */  string ReadLine(string filename, int line)  {      int lines, i = 0;      string temp;      fstream file;          file.open(filename.c_str());      lines = CountLines(filename);        if (line <= 0)      {          return "Error 1: 行数错误,不能为0或负数。";      }      if (file.fail())      {          return "Error 2: 文件不存在。";      }      if (line > lines)      {          return "Error 3: 行数超出文件长度。";      }      while (getline(file, temp) && i < line - 1)      {          i++;      }      file.close();      return temp;  }  

验证一下输出内容的正确性,需要注意的是C++项目会有默认工作路径,默认工作路径通常为项目文件*.vcproi 所在路径,将文件放在项目配置的调试工作路径下,那么读取文件只要写文件名称即可,如下所示。

#include<iostream>  #include<fstream>  #include<math.h>  #include <sstream>   using namespace std;  int main()  {      double arry[6] = { 10,20,30,40,50,60 };         cout << "读取数据为:" << ReadLine("TableC.txt",              GetNearestElementPosition(arry, 6, 32.15)) << endl;    system("pause");      return 0;  }

输出结果:

提取指定列的数据

现在读取的是一行数据,并且这行数据是以字符串的形式存储,如何从字符串中找到我们需要的数并把他转化成double类型的数据,这是我们接下来的需要进行的工作。

函数Extract()的作用是从用空格分割的字符串中提取指定位置的数据,并把数据转化为double类型,具体代码如下

/* * @brief  提取字符串中的数据 * @input  字符串、数据的位置 * @return double类型的数据 * @author YuHaoHao(1813481317@qq.com) * @date   2023-04-22 */  double Extract(string _chr, int _num)  {      string text = _chr;      size_t s = text.find_first_not_of(' ');      size_t e = text.find_last_not_of(' ');      stringstream ss(text.substr(s, e - s + 1));        double data;      double result[10];      int i = 0;      while (ss.good()) {          ss >> data;          result[i] = data;          i++;      }      return result[_num - 1];  }  

测试代码如下,arry是角度数组,arrx是lg(A)的数组,已知角度θ=32.15°(Theta=32.15),lg(A)=1.496(log10A=1.496)。通过函数GetNearestElementPosition()确定数据所在行和列,使用ReadLine()函数读取txt文档中指定行的字符串,再通过函数Extract ()读取字符串中指定列的数据,最后打印输出结果。​​​

int main()  {      double arry[6] = { 10,20,30,40,50,60 };      double arrx[9] = { 1,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8 };          int y = GetNearestElementPosition(arry, 6, 32.15);      int x = GetNearestElementPosition(arrx, 9, 1.496);        string char_ = ReadLine("data/TableC.txt", y);      double result = Extract(char_, x);      cout << "最终提取结果为:" << result << endl;          system("pause");      return 0;  }

输出结果如下:

整合函数

将上述步骤整合成一个函数FindNumber(),输入横轴参数、纵轴参数、横轴数组、纵轴数组、横轴数组个数、纵轴数组个数、读取文件名称,返回所需的参数。​

/* * @brief  提取经验表格中的指定数据 * @input  横轴参数、纵轴参数、横轴数组、           纵轴数组、横轴中数组个数、纵轴中数组个数、           读取的表格文件(.txt格式) * @return double类型,查找到的经验数据 * @author YuHaoHao(1813481317@qq.com) * @date   2023-04-22 */  double FindNumber(double _x, double _y, double arrx[],       double arry[], int numx, int numy, string _chr)  {      double result = 0;      int x = GetNearestElementPosition(arrx, numx, _x);      int y = GetNearestElementPosition(arry, numy, _y);      string chr = ReadLine(_chr, y);      result = Extract(chr, x);        return result;  }    int main()  {      double arry[6] = { 10,20,30,40,50,60 };      double arrx[9] = { 1,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8 };        double log10C = FindNumber(1.496, 32.15, arrx, arry,          9, 6, "data/TableC.txt");      cout << "查表得到的结果为:" << log10C << endl;      double C = pow(10, log10C);      cout << "参数C为:" << C << endl;        system("pause");      return 0;  }  

输出结果如下:

本文针对的是基于C语言进行公式转化时经常会遇到的二维数据索引问题,感谢哈工程(青岛)船舶工软团队余昊昊同学提供的解决思路,希望能够给您提供帮助!

已知前提条件有: 1. 内容是一个0到65535的数值. 2. 对应的二维表有很多个, 而且每个表的ROW COUNT和COLUMN COUNT都不固定. 3. 原始表格数据文件格式 a. 原始文件是文本格式, 文本行上表相对应. b. 每行由多个单词构成,单词之间由空格及制表符(TAB)的组合来分割. c. 第一行的单词用于定义COLUMN VALUE的命名 d. 从第二行开始, 每文本行对应二维表的一行. 第一个单词定义ROW VALUE的命名.从第二个单词开始, 每个单词均由数字构成, 描述二维表的内容. e. 空文本行表示二维表格结束. 根据上面的表格, 下面是原始数据: AAA BBB CCC DDD AAA 0 1 1 4 BBB 2 2 5 3 CCC 8 7 6 6 DDD 9 9 9 9 4. 输出格式要求: 生成C语言格式的描述. 包含ROW VALUE定义, COLUMN VALUE定义及二维数据结构.(根据上面的表格, 下面是个生成的例子) #define ROW_AAA 0 #define ROW_BBB 1 #define ROW_CCC 2 #define ROW_DDD 3 #define ROW_COUNT 4 #define COLUMN_AAA 0 #define COLUMN_BBB 1 #define COLUMN_CCC 2 #define COLUMN_DDD 3 #define COLUMN_COUNT 4 static <type> nuiTableData_a[][COLUMN_COUNT] = { 0, 1, 1, 4, 2, 2, 5, 3, 8, 7, 6, 6, 9, 9, 9, 9 }; 5. 原始表格数据一定是正确的, 不必考虑对原始数据的严格分析判断 6. 根据原始数据得出ROW COUNT和COLUMN COUNT 7. COLUMN VALUE从0开始, 依次递增. 8. ROW VALUE的定义和COLUMN VALUE定义类似, 但需要进行压缩判断.如果两列内容相同, ROW VALUE值也应该一样. 例如原始数据: … PPP 0 0 1 1 2 RRR 1 2 2 1 5 QQQ 0 1 2 3 4 SSS 1 2 2 1 5 TTT 9 9 8 8 7 … 那么生成的结果应是: … #define ROW_PPP 10 #define ROW_RRR 11 #define ROW_QQQ 12 #define ROW_SSS ROW_RRR #define ROW_TTT 13 … 题目: 编写一个程序。 读取一个文本表格文件, 并生成另一种格式的文件(C语言格式的H文件), 同时将表格的行列索引(AAA, BBB, CCC…,即ROW VALUE, COLUMN VALUE)以C语言的格式进行定义.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FastCAE2022

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值