将所有行从文本文件保存到对象的正确方法是什么。我有像这样的.txt文件
0001Marcus Aurelius 20021122160 21311
0002William Shakespeare 19940822332 11092
0003Albert Camus 20010715180 01232
从这个文件中我知道写入文件的每个数据的位置,并且所有数据都被格式化。
Line number is from 0 to 3
Book author is from 4 to 30
Publish date is from 31 to 37
Page num. is from 38 to 43
Book code is from 44 to 49
我创建了类数据,其中包含有关开始,结束位置,值,错误的信息。
然后我制作了包含Data类型列表的类Line,以及包含从某行创建的所有错误的列表。从行到对象加载数据后,数据I循环遍历lineError并从所有行添加错误,因为我需要将每行的错误保存到数据库。
我的问题是这种将数据从文件保存到对象的正确方法,以及在将相同数据保存到数据库之后,建议采用更好的方法吗?
public class Data
{
public int startPosition = 0;
public int endPosition = 0;
public object value = null;
public string fieldName = "";
public Error error = null;
public Data(int start, int end, string name)
{
this.startPosition = start;
this.endPosition = end;
this.fieldName = name;
}
public void SetValueFromLine(string line)
{
string valueFromLine = line.Substring(this.startPosition, this.endPosition - this.startPosition);
// if else statment that checks validity of data (lenght, empty value)
this.value = valueFromLine;
}
}
public class Line
{
public List<Data> lineData = new List<Data>();
public List<Error> lineError = new List<Error>();
public Line()
{
AddObjectDataToList();
}
public void AddObjectDataToList()
{
lineData.Add(new Data(0, 3, "lineNumber"));
lineData.Add(new Data(4, 30, "bookAuthor"));
lineData.Add(new Data(31, 37, "publishData"));
lineData.Add(new Data(38, 43, "pageNumber"));
lineData.Add(new Data(44, 49, "bookCode"));
}
public void LoadLineDataToObjects(string line)
{
foreach(Data s in lineData)
{
s.SetValueFromLine(line);
}
}
public void GetAllErrorFromData()
{
foreach (Data s in lineData)
{
if(s.error != null)
{
lineError.Add(s.error);
}
}
}
}
public class File
{
public string fileName;
public List<Line> lines = new List<Line>();
}
答案 0 :(得分:0)
我认为重点是使用OOP。我还假设解析是次要任务,我不会考虑其实现的选项。
首先,有必要确定主要作用对象。看起来很奇怪,这不是Book
,而是字符串本身(例如DataLine
)。最初,我想从字符串创建Book
(通过单独的构造函数),但那将是一个错误。
哪些操作应该能够执行DataLine
? - 事实上,只有一个 - process
。我看到这个方法有两个可接受的选项:
process
返回Book
或会引发异常。 (Book process()
)
process
不返回任何内容,但会与其他对象进行交互。 (void process(IResults result)
)
第一个选项有以下缺点:
很难测试(虽然这适用于第二种选择)。所有验证都隐藏在DataLine
内。
返回一些错误是不可能/困难的。
该计划旨在处理不正确的数据,因此通常会生成预期的例外情况。这违反了例外意识形态。此外,人们担心性能会下降。
第二种选择没有最后两个缺点。 IResults
可以包含方法error(...)
,以返回多个错误,以及success(Book book)
。
通过添加process
可以显着提高IValidator
方法的可测试性。此对象可以作为参数传递给DataLine
构造函数,但这并不完全正确。首先,这种不必要的记忆费用因为它不会给我们带来实实在在的好处。其次,这与DataLine
类的本质不符。 DataLine
仅表示可以以特定方式处理的行。因此,一个好的解决方案是void process (IValidator validator, IResults result)
。
总结以上内容(可能包含语法错误):
interface IResults {
void error (string message);
void success (Book book);
}
interface IValidator {
// just example
bool checkBookCode (string bookCode);
}
class DataLine {
private readonly string _rawData;
// constructor
/////////////////
public void process (IValidator validator, IResults result) {
// parse _rawData
bool isValid = true; // just example! maybe better to add IResults.hasErrors ()
if (! validator.checkBookCode (bookCode)) {
result.error("Bad book code");
isValid = false;
}
if (isValid) {
result.success(new Book (...));
// or even result.success (...); to avoid cohesion (coupling?) with the Book
}
}
}
下一步是使用线条创建文件模型。这里有很多选择和细微差别,但我想关注IEnumerable<DataLine>
。理想情况下,我们需要创建一个DataLines
类,该类将支持IEnumerable<DataLine>
并从文件或IEnumerable<string>
加载。然而,这种方法相对复杂和冗余,仅在大型项目中才有意义。一个更简单的版本:
interface DataLinesProvider {
IEnumerable <DataLine> Lines ();
}
class DataLinesFile implements DataLinesProvider {
private readonly string _fileName;
// constructor
////////////////////
IEnumerable <DataLine> Lines () {
// not sure that it's right
return File
. ReadAllLines (_fileName)
.Select (x => new DataLine (x));
}
}
你可以无限地改进代码,引入新的和新的抽象,但在这里你必须从常识和特定问题开始。
P上。 S.对不起“奇怪的”英语。 Google并不总是正确地翻译这些复杂的主题。