C# 解析txt文件,经典问题!

gidiyis 2010-02-02 11:36:47
以下是一个.txt文件内容:

abc|10|测试1|2|备注|
abc|11|测试2|3|备注|
aa|10|1|良好商品|
aa|10|1|良好商品|
aa|11|1|不良商品|
aa|11|1|良好商品|
aa|11|1|良好商品|

如上所示:
abc表示该行描述的是一个产品,10/11代表产品编号,2表示产品数量
aa表示该行是对产品的描述信息(产品良好或不良),10/11来自abc编号,1表示产品数量
产品实体类product,有如下字段:
id,name,count(数量),remarks,goodproductNo(良品数量),badproductNo(次品数量)

问题是:如何用程序来分别统计编号为10,11的产品到底有多少良品,多少不良品,然后赋
值给实体类product,

(如果嫌分不够可以另加分!)
...全文
1712 44 打赏 收藏 转发到动态 举报
写回复
用AI写文章
44 条回复
切换为时间正序
请发表友善的回复…
发表回复
wbxiaozhong 2011-04-12
  • 打赏
  • 举报
回复
三楼强悍
xiaozhi170 2010-04-30
  • 打赏
  • 举报
回复
3楼 正解
SmileWangCn 2010-04-28
  • 打赏
  • 举报
回复

/// <summary>
/// 读入一个pds导出的管线文件,通过参数返回
/// </summary>
/// <param name="FilePath">文件路径</param>
/// <param name="data">数据</param>
/// <param name="columns">列表栏位</param>
/// <param name="Msg">消息,成功读取时为空,由于考虑需要一次对整个文件进行检验,所以没有直接抛出异常,而使用msg返回</param>
/// <returns>bool</returns>
public bool PipingFileReader(string FilePath, out List<Dictionary<string, string>> data, out List<string> columns, out string Msg)
{
string strLine;
string strHeader;
string strDataArea;
string[] strArray;
char[] charArray = new char[] {'|'};
int RowCnt = 0;
int cntHeaderColumns;
int cntDataAreaCloums;
int cntDataAreaRows = 0;
//初始返回
data = new List<Dictionary<string, string>>();
columns = new List<string>();
Msg = "";

try
{
FileStream aFile = new FileStream(FilePath, FileMode.Open);
StreamReader sr = new StreamReader(aFile,Encoding.Default);//使用默认编码,防止乱码

// Obtain the columns from the first line

// Split row of data into string array
strLine = sr.ReadLine();
RowCnt++;
//顺序读取直到表头区域
while (strLine != null)
{
if (IsHeaderRow(strLine))
{
break;
}
strLine = sr.ReadLine();
RowCnt++;
}
//对数据表头进行检查
if (CheckTableHeader(strLine))
{
strHeader = TrimHeaderRow(strLine);
strArray = strHeader.Split(charArray);
cntHeaderColumns = strArray.GetUpperBound(0);//GetUpperBound(0) 返回 Array 的第一维的索引上限,GetUpperBound(Rank - 1) 返回 Array 的最后一维的上限。此方法的运算复杂度为 O(1)。
columns.Add("行号");//记录该行数据在源文件中的行号
for (int x = 0; x <= cntHeaderColumns; x++)
{
columns.Add(strArray[x].Trim());
}
}
else
{
Msg = "错误的单线材料表表头\r\n";
return false;
}
strLine = sr.ReadLine();
RowCnt++;
while (strLine != null)
{
if (IsDataRow(strLine))
{//如果遇到数据行
// Split row of data into string array
strDataArea = TrimDataRow(strLine);
strArray = strDataArea.Split(charArray);
Dictionary<string, string> dataRow = new Dictionary<string, string>();
cntDataAreaCloums=strArray.GetUpperBound(0);
if (cntDataAreaCloums != cntHeaderColumns)
{
Msg += "第" + RowCnt + "数据格式错误\r\n";
}
else
{
dataRow.Add("行号", RowCnt.ToString());//记录该行数据在源文件中的行号
for (int x = 0; x <= cntDataAreaCloums; x++)
{ //将索引该为1,0号位已经改为“行号”
dataRow.Add(columns[x + 1], strArray[x].Trim());
//但是此索引任然应该从0开始
}
data.Add(dataRow);
cntDataAreaRows++;
}
}
strLine = sr.ReadLine();
RowCnt++;
}

sr.Close();
if (Msg == "")
{
if (cntDataAreaRows % 2 == 0)
{
return true;
}
else
{
Msg = "数据区异常:没有保存数据区的2行缺省\r\n";
return false;
}
}
else
{
return false;
}
}
catch (IOException ex)
{
Msg += ex.ToString();
return false;
}
}

/// <summary>
/// 对单线转换为datatable的预处理
/// </summary>
/// <param name="Path">文件路径</param>
/// <param name="Msg">错误消息</param>
/// <returns></returns>
public DataTable PrePipingTable(string Path, out string Msg)
{
List<Dictionary<string, string>> data;
List<string> columns;
DataTable dt = new DataTable();
bool IsSuccess = PipingFileReader(Path, out data, out columns,out Msg);
if (!IsSuccess)
{
return dt;
}

foreach (string column in columns)
{
dt.Columns.Add(column,System.Type.GetType("System.String"));
}

foreach (Dictionary<string, string> row in data)
{
DataRow dr = dt.NewRow();
foreach (string clolumn in columns)
{
dr[clolumn] = row[clolumn];
}
dt.Rows.Add(dr);
}
return dt;
}

给你个参考,这个是我写的读pds单线材料表的类,格式和你这个差不多,只是有验证,所以你稍微删除修改下就可以把你那个文件读到datatable了,想做什么都统计都好办了。可以通用的。
SmileWangCn 2010-04-28
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 ytc666 的回复:]

读入到DataTable,再筛选
[/Quote]
这个可以的。
shadow_sdn 2010-03-12
  • 打赏
  • 举报
回复
rtyrhye
zhxingway 2010-02-03
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 ojlovecd 的回复:]
C# codestring[] temp= System.IO.File.ReadAllLines("E:\\aaa.txt", System.Text.Encoding.GetEncoding("gb2312"));
Dictionary<int, product> dic=new Dictionary<int, product>();foreach (string si?-
[/Quote]

厉害,支持,顶.
threenewbee 2010-02-03
  • 打赏
  • 举报
回复
ls正解
我姓区不姓区 2010-02-03
  • 打赏
  • 举报
回复

string[] temp = System.IO.File.ReadAllLines("E:\\aaa.txt", System.Text.Encoding.GetEncoding("gb2312"));
Dictionary<int, product> dic = new Dictionary<int, product>();
foreach (string s in temp)
{
if (s.StartsWith("abc"))
{
string[] pInfo = s.Split('|');
int id = int.Parse(pInfo[1]);
if (!dic.ContainsKey(id))
{
product p = new product();
p.id = id;
p.name = pInfo[2];
p.count = int.Parse(pInfo[3]);
p.remarks = pInfo[4];
dic.Add(id, p);
}
}
else if (s.StartsWith("aa"))
{
string[] qInfo = s.Split('|');
int id = int.Parse(qInfo[1]);
if (dic.ContainsKey(id))
{
product p = dic[id];
int count = int.Parse(qInfo[2]);
string quality = qInfo[3];
if (quality == "良好商品")
p.goodproductNo += count;
else if (quality == "不良商品")
p.badproductNo += count;
}
}
}
foreach (int i in dic.Keys)
{
product p = dic[i];
Console.WriteLine("Id:{0},name:{1},count:{2},remarks:{3},goodproductNo:{4},badproductNo:{5}", p.id, p.name, p.count, p.remarks, p.goodproductNo, p.badproductNo);
}


/*
输出:
Id:10,name:测试1,count:2,remarks:备注,goodproductNo:2,badproductNo:0
Id:11,name:测试2,count:3,remarks:备注,goodproductNo:2,badproductNo:1

*/
lzsh0622 2010-02-03
  • 打赏
  • 举报
回复
楼主的实际问题一定不会是这几行数据了.

可以把这两类数据建成三个数据表
表1: 试样信息 aa|10|1|良好商品| ...
表2: 产品信息 abc|10|测试1|2|备注| ...
表3: 产品统计 id,name,count,remarks,goodproductNo,badproductNo

用数据库解决统计问题就很容易了。

gidiyis 2010-02-03
  • 打赏
  • 举报
回复
ojlovecd (三楼)确实是高人啊,谢谢你的解答,我的问题解决了
suners 2010-02-03
  • 打赏
  • 举报
回复
-[
cuike519 2010-02-03
  • 打赏
  • 举报
回复
如果文件(记录的数量)不大,可以一次装入内存再分析,3楼的方法可以。

如果文件过大,3楼的方法可能运行会失败。

如果3楼能解决楼主的问题那就这样吧,如果不能还需要更好的方法来解决。
十八道胡同 2010-02-03
  • 打赏
  • 举报
回复
对txt硬搞不是好办法吧,最好以这个txt为数据库来操作

用txt做数据源连接字符串

//text作为数据源的链接字符串
string strConnection = @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\\test;
Extended Properties=""text;HDR=Yes;FMT=Delimited"";";

注意:c:\\test是个文件夹,里面有相应的txt文档做数据源。

xiaoshen1360 2010-02-03
  • 打赏
  • 举报
回复
学习
c520120 2010-02-03
  • 打赏
  • 举报
回复
如果两数据分开的话就比较好了
c520120 2010-02-03
  • 打赏
  • 举报
回复
INIClass

/// <summary>
/// ini文件操作
/// </summary>
public class INIClass
{
public string inipath;
[DllImport("kernel32")]
private static extern long WritePrivateProfileString(string section, string key, string val, string filePath);
[DllImport("kernel32")]
private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath);
/// <summary>
/// 构造方法
/// </summary>
/// <param name="INIPath">文件路径</param>
public INIClass(string INIPath)
{
inipath = INIPath;
}
/// <summary>
/// 写入INI文件
/// </summary>
/// <param name="Section">项目名称(如[TypeName] )</param>
/// <param name="Key">键</param>
/// <param name="Value">值</param>
public void IniWriteValue(string Section, string Key, string Value)
{
WritePrivateProfileString(Section, Key, Value, this.inipath);
}
/// <summary>
/// 读出INI文件
/// </summary>
/// <param name="Section">项目名称(如[TypeName] )</param>
/// <param name="Key">键</param>
public string IniReadValue(string Section, string Key)
{
StringBuilder temp = new StringBuilder(500);
int i = GetPrivateProfileString(Section, Key, "", temp, 500, this.inipath);
return temp.ToString();
}
}
c520120 2010-02-03
  • 打赏
  • 举报
回复
static void Main(string[] args)
{
string FilePath = @"C:\Users\chenjun\Desktop";

INIClass iniMake = new INIClass(@"C:\Users\chenjun\Desktop\Schema.ini");

iniMake.IniWriteValue("11.txt", "Format", "Delimited(|)");
iniMake.IniWriteValue("11.txt", "ColNameHeader", "false");
iniMake.IniWriteValue("11.txt", "col1", "RowType TEXT WIDTH 100 ");
iniMake.IniWriteValue("11.txt", "col2", "ProductID TEXT WIDTH 100 ");
iniMake.IniWriteValue("11.txt", "col3", "Colmn1 TEXT WIDTH 100");
iniMake.IniWriteValue("11.txt", "col4", "Colmn2 TEXT WIDTH 100 ");
iniMake.IniWriteValue("11.txt", "col5", "Colmn3 TEXT WIDTH 100 ");
string constr = @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source='" + FilePath + "';Extended Properties='text;HDR=No;FMT=Delimited(|);'";
OleDbDataAdapter da = new OleDbDataAdapter();
OleDbConnection OConn = new OleDbConnection();
OConn.ConnectionString = constr;
OConn.Open();


OleDbCommand OCmm = new OleDbCommand();
OCmm.Connection = OConn;
da.SelectCommand = OCmm;
OCmm.CommandTimeout = 0;
DataTable ProtuctTable = new DataTable();
DataTable QualityTable = new DataTable();
OCmm.CommandText = "Select ProductID,Colmn1 as ProductName,Colmn2 as ProductCount,Colmn3 as ProductComment from [11.txt] where RowType='abc'";
da.Fill(ProtuctTable);
OCmm.CommandText = "Select ProductID,Sum(Colmn1) as Amount,Colmn2 as ProductMass from [11.txt] where RowType='aa' Group By ProductID,Colmn2";
da.Fill(QualityTable);
da.Dispose();
OCmm.Dispose();
OConn.Dispose();
foreach (DataRow dr in ProtuctTable.Rows)
{
DataRow[] rows = QualityTable.Select("ProductID=" + dr["ProductID"]);
foreach (DataRow row in rows)
{
Console.WriteLine(dr["ProductID"] + " " + dr["ProductName"] + " " + row["ProductMass"] + " " + row["Amount"]);
}
}
Console.ReadLine();
}
ckl881003 2010-02-03
  • 打赏
  • 举报
回复
直接用正则找吧。。当然效率就不好说了~
vrhero 2010-02-03
  • 打赏
  • 举报
回复
[Quote=引用 23 楼 c520120 的回复:]
用OLEDB 连接TXT文件 用sql语句 查询


载入文件前创建txt分割相关的配置文件
[/Quote]
这才是正解...虽然效率也一般,但在数据源不能改变时这个才能叫解决方案...

用ReadAllLines这个txt文件很大怎么办?数据源改变时怎么办?重ReadAllLines?
加载更多回复(24)

111,120

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • AIGC Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧