I have a text parser that reads certain information from a given file with a specified format. The text file contains some measured properties of a product. The date model number/lot number is written on the file name.
class SomeTextFileParser
{
private static int POINT_COUNT = 15;
string filePath;
string date;
string modelName;
string lotName;
string fileDate;
string fileModelName;
string fileLotName;
public string errMessage;
public List<EqusData> dataList = new List<EqusData>();
private bool ErrorReturn(string message)
{
this.errMessage += message + " ";
return false;
}
public SomeTextFileParser(string path)
{
this.filePath = path;
this.errMessage = "";
}
public bool Parse()
{
try
{
string[] lines;
if (!File.Exists(this.filePath))
return ErrorReturn("File does not exist.");
lines = System.IO.File.ReadAllLines(this.filePath, Encoding.GetEncoding(949));
if (lines.Length < 1)
return ErrorReturn("File is empty.");
if (!ParseFilePath())
return ErrorReturn("");
if (!ParseHeaderLine(lines[0]))
return ErrorReturn("");
if (!modelName.Equals(fileModelName) || !lotName.Equals(fileLotName))
return ErrorReturn("File name does not correspond with the model/lot number in the content.");
this.dataList.Clear();
foreach (string line in lines)
{
string[] valueLineParts = line.Split(',');
if (valueLineParts.Length != POINT_COUNT + 7)
{
continue;
}
if (!ParseValueLine(line))
{
this.dataList.Clear();
return ErrorReturn("");
}
}
if(this.dataList.Count == 0)
return ErrorReturn("No input data found.");
return true;
}
catch (Exception e)
{
return ErrorReturn("exception:" + e.Message);
}
}
private bool ParseFilePath()
{
string[] filePathParts = this.filePath.Split('\\');
if (filePathParts.Length < 2) return ErrorReturn("Invalid file path.");
string fileDay = filePathParts[filePathParts.Length - 2].Substring(0, 2);
string fileName = filePathParts.Last();
string[] fileNameParts = fileName.Split('_');
if (fileNameParts.Length != 3) return ErrorReturn("Invalid file name format.");
this.fileDate = "20" + fileNameParts[0].Substring(0, 4) + fileDay;
this.fileModelName = fileNameParts[1];
this.fileLotName = fileNameParts[2].Replace(".txt", "");
if (this.fileDate.Length != 8 || this.fileModelName.Length < 1 || this.fileLotName.Length < 1) return ErrorReturn("Unable to determine Model/Lot number from file name.");
this.date = this.fileDate;
return true;
}
private bool ParseHeaderLine(string line)
{
int modelNameStartPoint, modelNameEndPoint, lotNameStartPoint;
modelNameStartPoint = line.IndexOf("Model Name:");
modelNameEndPoint = line.IndexOf(",");
lotNameStartPoint = line.IndexOf("LOT Name:");
if (modelNameStartPoint < 0 || modelNameEndPoint < 0 || lotNameStartPoint < 0)
return ErrorReturn("Unable to determine Model/Lot number from file name.");
modelNameStartPoint += ("Model Name:").Length;
lotNameStartPoint += ("LOT Name:").Length;
this.modelName = line.Substring(modelNameStartPoint, modelNameEndPoint - modelNameStartPoint);
this.lotName = line.Substring(lotNameStartPoint);
return true;
}
private bool ParseValueLine(string line)
{
line = line.Replace(" ", "");
string[] valueLineParts = line.Split(',');
if (valueLineParts.Length != POINT_COUNT + 7)
return ErrorReturn("Invalid point count.");
EqusData equsData = new EqusData();
equsData.date = this.date;
equsData.lotName = this.lotName;
equsData.modelName = this.modelName;
equsData.sequence = -1;
if (!Int32.TryParse(valueLineParts[0], out equsData.sequence))
return ErrorReturn("Invalid data sequence.");
// Add value per point
for(int i = 1; i <= POINT_COUNT; i++)
equsData.valueList.Add(valueLineParts[i]);
// Add average value to the list
equsData.valueList.Add(valueLineParts[POINT_COUNT + 3]);
equsData.isGood = valueLineParts[POINT_COUNT + 4].Equals("G");
equsData.usl = valueLineParts[POINT_COUNT + 5];
equsData.lsl = valueLineParts[POINT_COUNT + 6];
this.dataList.Add(equsData);
return true;
}
Data Class
class EqusData
{
public string date;
public string modelName;
public string lotName;
public int sequence;
public bool isGood;
public string usl;
public string lsl;
public List<string> valueList = new List<string>();
}
This class is most likely to be used in the following manner:
SomeTextFileParser fileParser = new SomeTextFileParser("C:\SomeText.txt");
if (!fileParser.Parse())
{
MessageBox.Show("Error: " + fileParser.errMessage);
return;
}
//Do something with SomeTextFileParser.dataList here...
I'd like to be critiqued on how readable the above code is, but please let me know if my initial thoughts about the code is more or less right:
Shouldn't the
errMessage
be a public property instead of public variable, although in practice it doesn't make any distinction here?Or even better, shouldn't the parse method follow the
TryParse
pattern and send the error message as out parameter?--> If this is the case, should the inner parse methods (i.e.
ParseFilePath
) also follow the sameTryParse
convention even though they are private methods only used in this class?Although the inner parse methods use private variables in the class, shouldn't a data class be made containing those variables? For example,
ParseFilePath
setsfileDate
,fileModelName
andfileLotName
variables to a value, but I have no way of knowing that from just looking at the Parse method. Would it be more clear if I returned an object containing those information fromParseFilePath
, and pass that as a parameter to other methods requiring that information? This on the other hand, seems like an overkill.
I'd really appreciate any thoughts or comments. Be as harsh as you need to be.