前言
开始考虑写一些工具是因为有一个医学生的女朋友,她做实验,收集Pet数据之后,需要将这些数据转换成 CR、PR、PD、SD、NE这些关键术语。有点计算的东西,然后她又很笨,没办法啦,就给准备给她做一个工具来计算。
在网上搜了一下( CR、PR、PD、SD),找到一个靶病灶的评价跟她说的差不多:
1.完全缓解(CR)。所有靶病灶消失。
2.部分缓解(PR)。靶病灶最长径之和与基线状态比较,至少减少30%。
3.病变进展(PD)。靶病灶最长径之和与治疗开始之后所记录到的最小的靶病灶最长径之和比较,增加20%,或者出现一个或多个新病灶。
4.病变稳定(SD)。介于部分缓解和疾病进展之间。
然后写了一个很low的窗口程序给她。如图:
注:这个是她给我说有问题的图,因为默认值是-1,pet-Dmax1(cm)是默认值,然后就算成-1了。
之所以说这个low,是因为数据不止这点,用的Excel保存的,如果每个都这样输入的话,好麻烦啊,这样的就只能一次验证一组数据。所以开始考虑学习一下怎么直接读取Excel,然后导出需要的数据。
准备
核心思路是先通过文件路径读取Excel,然后处理完数据之后,导出到文件夹里面。百度了一下Excel的读写,想了一下之前用过csv,所以就决定使用csv来做表格。后边再做优化吧,一步一步来。
一、导入文件
首先需要文件路径,一种方法是直接填写文件路径,另一种是通过选择文件的形式获取文件路径。这里就需要用到C#打开文件夹的函数OpenFileDialog。
OpenFileDialog dialog = new OpenFileDialog();
dialog.Multiselect = false;//该值确定是否可以选择多个文件
dialog.Title = "请选择文件夹";
dialog.Filter = "所有文件(*.csv)|*.csv";
//C#获取当前程序运行路径的方法集合(参考博客里面推荐用法)
string _path = System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
dialog.InitialDirectory = _path;
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
string filePath = dialog.FileName;
this.richTextBox1.Text = filePath;
}
这样可以获取到文件的路径,接下来就是读取文件了。
二、读取CSV文件
这里使用的是别人写的代码,做了点小的修改,判断文件编码类型那个没有看,直接用别人的源码,(因为直接用UTF-8的话,中文还是会出现乱码,调试看了一下,读出来的编码格式不是UTF-8),将CSV文件读取来存到DataTable中。
/// <summary>
/// 将CSV文件的数据读取到DataTable中
/// </summary>
/// <param name="fileName">CSV文件路径</param>
/// <returns>返回读取了CSV数据的DataTable</returns>
public static DataTable OpenCSV(string filePath)
{
DataTable dt = new DataTable();
try
{
FileStream fs = new FileStream(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read);
Encoding encoding = GetType(fs);
// fs = new FileStream(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read);
StreamReader sr = new StreamReader(filePath, encoding);
//记录每次读取的一行记录
string strLine = "";
//记录每行记录中的各字段内容
string[] aryLine = null;
string[] tableHead = null;
//标示列数
int columnCount = 0;
//标示是否是读取的第一行
bool IsFirst = true;
//逐行读取CSV中的数据
while ((strLine = sr.ReadLine()) != null)
{
if (IsFirst == true)
{
tableHead = strLine.Split(',');
IsFirst = false;
columnCount = tableHead.Length;
//创建列(第一行的所有值当可以,不能重复)
for (int i = 0; i < columnCount; i++)
{
DataColumn dc = new DataColumn(tableHead[i]);
if (dt.Columns.Contains(tableHead[i]))
{
System.Diagnostics.Debug.WriteLine("表的第一行有重复的Key:" + tableHead[i]);
continue;
}
dt.Columns.Add(dc);
}
}
else
{
aryLine = strLine.Split(',');
DataRow dr = dt.NewRow();
for (int j = 0; j < columnCount; j++)
{
dr[j] = aryLine[j];
}
dt.Rows.Add(dr);
}
}
if (aryLine != null && aryLine.Length > 0)
{
dt.DefaultView.Sort = tableHead[0] + " " + "asc";
}
sr.Close();
fs.Close();
}
catch (Exception)
{
MessageBox.Show("检查一下路径:\n" + filePath + "\n是否正在使用或不正确!");
throw;
}
return dt;
}
/// 通过给定的文件流,判断文件的编码类型
/// <param name="fs">文件流</param>
/// <returns>文件的编码类型</returns>
public static System.Text.Encoding GetType(System.IO.FileStream fs)
{
byte[] Unicode = new byte[] { 0xFF, 0xFE, 0x41 };
byte[] UnicodeBIG = new byte[] { 0xFE, 0xFF, 0x00 };
byte[] UTF8 = new byte[] { 0xEF, 0xBB, 0xBF }; //带BOM
System.Text.Encoding reVal = System.Text.Encoding.Default;
System.IO.BinaryReader r = new System.IO.BinaryReader(fs, System.Text.Encoding.Default);
int i;
int.TryParse(fs.Length.ToString(), out i);
byte[] ss = r.ReadBytes(i);
if (IsUTF8Bytes(ss) || (ss[0] == 0xEF && ss[1] == 0xBB && ss[2] == 0xBF))
{
reVal = System.Text.Encoding.UTF8;
}
else if (ss[0] == 0xFE && ss[1] == 0xFF && ss[2] == 0x00)
{
reVal = System.Text.Encoding.BigEndianUnicode;
}
else if (ss[0] == 0xFF && ss[1] == 0xFE && ss[2] == 0x41)
{
reVal = System.Text.Encoding.Unicode;
}
fs.Close();
r.Close();
return reVal;
}
/// 判断是否是不带 BOM 的 UTF8 格式
/// <param name="data"></param>
/// <returns></returns>
private static bool IsUTF8Bytes(byte[] data)
{
int charByteCounter = 1; //计算当前正分析的字符应还有的字节数
byte curByte; //当前分析的字节.
for (int i = 0; i < data.Length; i++)
{
curByte = data[i];
if (charByteCounter == 1)
{
if (curByte >= 0x80)
{
//判断当前
while (((curByte <<= 1) & 0x80) != 0)
{
charByteCounter++;
}
//标记位首位若为非0 则至少以2个1开始 如:110XXXXX...........1111110X
if (charByteCounter == 1 || charByteCounter > 6)
{
return false;
}
}
}
else
{
//若是UTF-8 此时第一位必须为1
if ((curByte & 0xC0) != 0x80)
{
return false;
}
charByteCounter--;
}
}
if (charByteCounter > 1)
{
throw new Exception("非预期的byte格式");
}
return true;
}
}
三、修改csv数据
修改数据这里,我是将读取的DataTable数据存在二维数组里面的,因为我要一列一列的使用数据,使用完了之后,再将二维数组的数据存入一个新的DataTable里面。
1、存入数组:DataTable里面的Columns存的是表的第一行所有列的数据,相当于key,Rows里面没有这些数据。
//第一行 所有列
for (int i = 0; i < m_curDataTable.Columns.Count; i++)
{
DataTableArr[0, i] = m_curDataTable.Columns[i].ToString();
}
for (int r = 0; r < m_curDataTable.Rows.Count; r++)
{
for (int c = 0; c < m_curDataTable.Columns.Count; c++)
{
DataTableArr[r+1, c] = m_curDataTable.Rows[r][c].ToString();
}
}
2、修改数据:按要求处理数组。
3、存入DataTable:存和取都是一样的,第一行需要单独处理。
m_destinationDataTable.Columns.Clear();
m_destinationDataTable.Rows.Clear();
//创建第一行
for (int i = 0; i < m_curDataTable.Columns.Count; i++)
{
DataColumn dc = new DataColumn(DestinationDataTableArr[0, i]);
if (m_destinationDataTable.Columns.Contains(DestinationDataTableArr[0, i]))
{
MessageBox.Show("第一行有相同的文字!");
return;
}
m_destinationDataTable.Columns.Add(dc);
}
for (int r = 0; r < m_curDataTable.Rows.Count; r++)
{
DataRow dr = m_destinationDataTable.NewRow();
for (int c = 0; c < m_curDataTable.Columns.Count; c++)
{
dr[c] = DestinationDataTableArr[r + 1, c];
}
m_destinationDataTable.Rows.Add(dr);
}
四、导出文件
将DataTable数据写入CSV文件。
/// <summary>
/// 将DataTable中数据写入到CSV文件中
/// </summary>
/// <param name="dt">提供保存数据的DataTable</param>
/// <param name="fileName">CSV的文件路径</param>
public static void SaveCSV(DataTable dt, string fullPath)
{
try
{
FileInfo fi = new FileInfo(fullPath);
if (!fi.Directory.Exists)
{
fi.Directory.Create();
}
FileStream fs = new FileStream(fullPath, System.IO.FileMode.Create, System.IO.FileAccess.Write);
StreamWriter sw = new StreamWriter(fs, System.Text.Encoding.UTF8);
string data = "";
//写出列名称
for (int i = 0; i < dt.Columns.Count; i++)
{
data += dt.Columns[i].ColumnName.ToString();
if (i < dt.Columns.Count - 1)
{
data += ",";
}
}
sw.WriteLine(data);
//写出各行数据
for (int i = 0; i < dt.Rows.Count; i++)
{
data = "";
for (int j = 0; j < dt.Columns.Count; j++)
{
string str = dt.Rows[i][j].ToString();
str = str.Replace("\"", "\"\"");//替换英文冒号 英文冒号需要换成两个冒号
if (str.Contains(',') || str.Contains('"')
|| str.Contains('\r') || str.Contains('\n')) //含逗号 冒号 换行符的需要放到引号中
{
str = string.Format("\"{0}\"", str);
}
data += str;
if (j < dt.Columns.Count - 1)
{
data += ",";
}
}
sw.WriteLine(data);
}
sw.Close();
fs.Close();
DialogResult result = MessageBox.Show("CSV文件保存成功!", "", MessageBoxButtons.OKCancel);
if (result == DialogResult.OK)
{
System.Diagnostics.Process.Start("explorer.exe", fullPath);
}
}
catch (Exception)
{
MessageBox.Show("检查一下路径:\n" + fullPath + "\n是否正在使用或不正确!");
throw;
}
}
其他
1、控制台打印
System.Diagnostics.Debug.WriteLine("Key:"+j+",Value:"+dr[j]);
2、FileInfo常用参数的使用(不用自己切割路径获取相关参数)
FileInfo fi = new FileInfo(fullPath);
new一个FileInfo对象之后,可以通过这个FileInfo对象查找到:
(1)、文件的扩展名(fi.Extension)
(2)、文件是否存在(fi.Exists)
(3)、文件带扩展名的名称(fi.Name)
然后通过fi.Directory可以查找到:
(1)、文件目录的父目录(fi.Parent)
(2)、文件目录是否存在(fi.Exists)
(3)、文件目录的名称(fi.Name)
(4)、文件目录的全路径(fi.FullName)
3、打开文件
System.Diagnostics.Process.Start("explorer.exe", fullPath);
4、消息盒子:winform的通用弹窗
MessageBox.Show("CSV文件保存成功!", "", MessageBoxButtons.OKCancel);
结果
最后就做成这样了,然后,她不用这个工具了,因为她已经算完数据了。0.0
工具说明:表格的第一行是填姓名的,不能重复,并且不进行计算:
链接:https://pan.baidu.com/s/132292ZpbZaQHZSEwM_yK0g
提取码:6ozx
复制这段内容后打开百度网盘手机App,操作更方便哦
参考blog: