C# 分析文本文件 - 按索引访问类字段
本文关键字:索引 访问 字段 文本 文件 | 更新日期: 2023-09-27 18:37:00
我想解析一个包含几十个条目的文本文件。现在,我有一个愚蠢的解决方案,可以逐行读取并与硬编码字符串进行比较:
while ((line = reader.ReadLine()) != null) //returns null if end of stream
{
cmpStr = "MODE";
try
{
if (line.Equals(cmpStr))
GlobalData.mode = Convert.ToInt32(line.Remove(0, cmpStr.Length));
}
catch { }
cmpStr = "TIME_YEAR";
try
{
if (line.Equals(cmpStr))
GlobalData.time_year = Convert.ToInt32(line.Remove(0, cmpStr.Length));
}
catch { }
// ... repeat to parse the remaining lines
}
GlobalData 是一个静态类,如下所示:
public static class GlobalData
{
public static int mode;
public static int time_year;
public static int time_month;
public static int time_day;
public static int time_hour;
public static int time_minute;
// other entries omitted
public static string[] GlobalKeywords = new string[37]
{
"MODE",
"TIME_YEAR",
"TIME_MONTH",
"TIME_DAY",
"TIME_HOUR",
"TIME_MINUTE",
// other entries omitted
};
}
如果可以按索引访问我的静态字段,我会这样做:
int i = 0;
while ((line = reader.ReadLine()) != null)
{
cmpStr = GlobalData.GlobalKeywords[i]; // when i == 0: cmpStr = "MODE"
if (line.Equals(cmpStr))
GlobalData[i] = Convert.ToInt32(line.Remove(0, cmpStr.Length));
// GlobalData[0] would be GlobalData.mode, and so on (but doesn't work)
i++;
}
catch { }
因此,即使我可以设置一个循环来与关键字的字符串数组进行比较,如何分配静态类的某个字段?
BR克里斯
我不确定您的业务限制是什么,因此很难提出一个万无一失的解决方案,尽管有几点:
cmpStr = "MODE"; try { if (line.Equals(cmpStr)) GlobalData.mode = Convert.ToInt32(line.Remove(0, cmpStr.Length)); }
这不会像您(可能预期的那样)工作 - 如果line.Equals("MODE")
则line.Remove(0, "MODE".Length)
是一个空字符串。您可能想要的是 line.StartsWith(cmpStr)
或line.Contains(cmpStr)
.
全局数据是一个静态类
对于您正在做的事情来说,这似乎不是一个好方法。您可能希望阅读静态类以及何时使用它们(MSDN 是一个很好的起点,尽管它显然不能涵盖所有内容:http://msdn.microsoft.com/en-us/library/79b3xss3%28v=vs.80%29.aspx)。
除此之外,您可能可以简单地将所有 int 字段替换为字典(尽管请重新考虑上述静态方法):
public static Dictionary<String, int> Items = new Dictionary<String, int>();
然后,您的解析代码可能如下所示:
while ((line = reader.ReadLine()) != null) //returns null if end of stream
{
var matchingString
= GlobalData.GlobalKeywords.FirstOrDefault(s => line.StartsWith(s));
if (matchingString != null)
GlobalData[matchingString]
= Convert.ToInt32(line.Remove(0, matchingString.Length));
}
然后,您将能够使用以下方法获取该数据: GlobalData.Items["MODE"]
.
最后一点:您可以考虑在全局数据类中引入常量值,例如:
public const String MODE = "MODE";
然后您可以使用GlobalData.Items[GlobalData.MODE]
并避免拼写错误:编写GlobalData.Items[GlobalData.MODe]
会导致编译错误。
替换这个:
public static int mode;
public static int time_year;
public static int time_month;
public static int time_day;
public static int time_hour;
public static int time_minute;
有了这个:
public static Dictionary<string, int> my_values = new Dictionary<string, int>();
然后替换:
GlobalData[i] = Convert.ToInt32(line.Remove(0, cmpStr.Length));
跟:
GlobalData.my_values[cmpStr] = Convert.ToInt32(line.Remove(0, cmpStr.Length));
这应该做你想做的事,即使我不明白你期望Convert.ToInt32
如何工作。您调用Remove
的方式将创建一个空字符串(可能会转换为 0,我不记得了),即使没有,该行也不包含数字,因为您成功地将其与"MODE"这样的字符串进行了比较。
解决问题的一种优雅方法是为每个可接受的字符串准备不同的操作。您使用Dictionary(Of String, <Action>)
其中 Action 是一种常见的委托类型,它在输入中接收字符串,并知道如何根据行首的关键字对其进行处理。
// The common signature for every methods stored in the value part of the dictionary
public delegate void ParseLine(string line);
// Global dictionary where you store the strings as keyword
// and the ParseLine as the delegate to execute
Dictionary<String, ParseLine> m_Actions = new Dictionary<String, ParseLine>() ;
void Main()
{
// Initialize the dictionary with the delegate corresponding to the strings keys
m_Actions.Add("MODE", new ParseLine(Task1));
m_Actions.Add("TIME_YEAR", new ParseLine(Task2));
m_Actions.Add("TIME_MONTH", new ParseLine(Task3));
m_Actions.Add("TIME_DAY", new ParseLine(Task4));
.....
while ((line = reader.ReadLine()) != null)
{
// Search the space that divide the keyword from the value on the same line
string command = line.Substring(0, line.IndexOf(' ')).Trim();
// a bit of error checking here is required
if(m_Actions.ContainsKey(command))
m_Actions[command](line);
}
}
void Task1(string line)
{
// this will handle the MODE line
GlobalData.Mode = Convert.ToInt32(line.Substring(line.IndexOf(' ')+1).Trim());
}
void Task2(string line)
{
GlobalData.time_year = Convert.ToInt32(line.Substring(line.IndexOf(' ')+1).Trim());
}
void Task3(string line)
{
.....
}
void Task4(string line)
{
.....
}
一种简单(但不是很干净)的方法是将索引器添加到全局数据类,并根据索引决定要设置的字段。但是每次添加字段时都必须扩展索引器(基本上是将 if/switch 从 while 循环移动到索引器中)。
如果可以将关键字与字段名称匹配,也可以使用反射。这性能不是很好,但只要您可以将关键字映射到新字段名称,就不需要扩展。
另一种方法是创建字典>。在此字典中,您注册关键字,例如(伪代码):类级别变量:
private keywordsDict = new Dictionary<string, Action<int>>();
在构造函数中:
keywordsDict.Add("MODE", delegate(value) GlobalData.mode = value);
在 while 循环中:
var action = keywordsDict[line];
action(value);
在后面的方法中,如果您有新的关键字/字段,则只需要扩展字典,而不需要扩展算法本身。
也许我可以告诉你如何在 C# 中实现它 (GlobalData[i]) 认为这不是你要找的答案。
class GlobalData
{
private string[] array = new string[10];
public GlobalData()
{
//now initialize array
array[0] = "SomeThingA";
array[1] = "SomeThingB";//continue initialization.
}
public string this[int index]
{
get {return array[index];}
}
}
现在客户端可以使用 GlobalData,例如 ,
GlobalData gd = new GlobalData();
gd[1] = "SomeOtherThing" ; //set the value.
string value = gd[1];//get the value
但这不能通过使类静态来完成,因为您看到它适用于"this"