【问题标题】:What is proper way to save data from file to object C#将数据从文件保存到对象 C# 的正确方法是什么
【发布时间】:2018-01-22 01:43:04
【问题描述】:

将所有行从文本文件保存到对象的正确方法是什么。我有类似这样的 .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,以及包含从某行创建的所有错误的列表。将数据从行加载到对象数据后,我循环通过 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>();
}

【问题讨论】:

  • 您可能想研究 序列化 - 如果它已保存到数据库,但为什么您还需要文本表单?请阅读How to Ask 并采取tour
  • 那么您的问题实际上是如何将文本文件解析到数据库中?
  • 不,我的问题是将数据从文件保存到对象的最佳方法是什么,因为在我将文件中的所有行保存到对象之后,我需要对数据进行一些验证,并且更容易循环第一行的所有数据并检查例如我的库中是否有作者数据,书籍代码等。如果某行没有我的数据库中的数据,我需要跳过将该行保存在数据库中。我将数据保存到数据库没有问题,效果很好。我只需要建议这个模型是否适合将数据从一行保存到对象并检查某些数据是否存在。
  • 不,谢谢,这似乎很有趣,我的文件不是 csv。我没有分隔符。正如您在上面的示例中看到的那样,我将行号和作者姓名连接在一起。我只知道文件中每个数据的位置在哪里,并且该位置是恒定的。

标签: c# file oop


【解决方案1】:

我假设重点是使用 OOP。我还假设解析是次要任务,我不会考虑其实现选项。

首先要确定主要的作用对象。看起来很奇怪,这不是Book,而是字符串本身(例如DataLine)。最初,我想从一个字符串(通过单独的构造函数)创建一个Book,但那将是一个错误。

DataLine 应该能够执行哪些操作? - 事实上,只有一个 - process。我看到此方法有两个可接受的选项:

  1. process 返回Book 抛出异常。 (Book process())

  2. process 不返回任何内容,但与另一个对象交互。 (void process(IResults result))

第一种方案有以下缺点:

  • 很难测试(尽管这适用于第二个选项)。所有验证都隐藏在 DataLine 中。

  • 返回一些错误是不可能/很难的。

  • 该程序旨在处理不正确的数据,因此经常会产生预期异常。这违反了例外的意识形态。此外,还有人担心会降低性能。

第二个选项没有最后两个缺点。 IResults 可以包含方法error(...),返回多个错误,success(Book book)

通过添加IValidator,可以显着提高process 方法的可测试性。该对象可以作为参数传递给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&lt;DataLine&gt;。理想情况下,我们需要创建一个支持IEnumerable&lt;DataLine&gt;DataLines 类,并从文件或IEnumerable&lt;string&gt; 加载。但是,这种方法相对复杂且冗余,仅在大型项目中才有意义。一个更简单的版本:

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 并不总是能正确翻译如此复杂的主题。

【讨论】:

  • 感谢您的建议,是的,您是正确的,重点是使用 OOP。对于验证,我有两种方法,一种用于在将数据保存到对象之前验证数据(检查 bookCode 的长度是否正确,并检查在该位置上是否有任何数据,在某些情况下我只能有空格),另一种验证当我有所有写入对象的数据检查 bookCode 是否在数据库中有数据。
  • all data data written in objects - 这不是 OOP。如果您创建Book,则它是有效的书。如果它不是比创建PossibleBook 有效的书,那将检查它是否有效并且仅比创建Book。另外,为什么不创建Book就不能查看bookCode?​​span>
  • 因为我认为如果我同时从文件中读取数据并检查我的数据库中是否有一些记录,它会降低性能。正如你所说,这可能会导致性能下降,有时我可以获得文件中包含超过 10 000 行甚至更多行的文本文件。
  • 我说过不必要的异常抛出可能会导致性能问题。如果您想从文件中加载所有数据并在我的代码已经完成此操作之后对其进行验证(DataLine 只需存储单行并仅按需验证)。如果您使用的文件非常大,则需要将其分成几块,甚至可以向数据库添加一些批量验证请求。
猜你喜欢
  • 2022-06-25
  • 2011-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-25
相关资源
最近更新 更多