1

以下のようなcsvファイルがあります。

Processname:;ABC Buying
ID:;31
Message Date:;08-02-2012

Receiver (code):;12345
Object code:


Location (code):;12345
Date;time
2012.02.08;00:00;0;0,00
2012.02.08;00:15;0;0,00
2012.02.08;00:30;0;0,00
2012.02.08;00:45;0;0,00
2012.02.08;01:00;0;0,00
2012.02.08;01:15;0;0,00

上記のメッセージが 1 回以上発生する可能性があるため、2 回発生した場合、csv ファイルは次のようになります...

Processname:;ABC Buying
ID:;31
Message Date:;08-02-2012

Receiver (code):;12345
Object code:


Location (code):;12345
Date;time
2012.02.08;00:00;0;0,00
2012.02.08;00:15;0;0,00
2012.02.08;00:30;0;0,00
2012.02.08;00:45;0;0,00
2012.02.08;01:00;0;0,00
2012.02.08;01:15;0;0,00
Processname:;ABC Buying
ID:;41
Message Date:;08-02-2012

Receiver (code):;12345
Object code:


Location (code):;12345
Date;time
2012.02.08;00:00;0;17,00
2012.02.08;00:15;0;1,00
2012.02.08;00:30;0;15,00
2012.02.08;00:45;0;0,00
2012.02.08;01:00;0;0,00
2012.02.08;01:15;0;9,00

このcsvファイルを解析する最良の方法は何ですか?

私のアプローチの疑似コード...

// Read the complete file
var lines = File.ReadAllLines(filePath);

// Split the lines at the occurrence of "Processname:;ABC Buying" 
var blocks = lines.SplitAtTheOccuranceOf("Processname:;ABC Buying");

// The results will go to
var results = new List<Result>();

// Loop through the collection
foreach(var b in blocks)
{
     var result = new Result();

      foreach(var l in b.lines)
      {

           // read the first line and check it contains "Processname" if so, assign the value to result.ProcessName = 

           // read the 2nd line and check it contains "ID" if so, assign the value to result.ID

           // read the 3rd line and check it contains "Object Code" if so, assign the value to result.ObjectCode

           // Ignore string.empty

           // check for location (code), if so assign the value to result.LocationCode

           // Parse all the other rows by spliting with ';' the first part is date, 2nd part is time, 3rd part is value


       }
      results.Add(result);
}

これを行う最良の方法は何ですか?

4

2 に答える 2

4

まず、これは CSV ファイルのようには見えません。次に、ファイル全体を 1 行ずつ読み取るだけです。オブジェクトの最初の行のように見える "Processname:;ABC Buying" のような行を取得したら、新しいオブジェクトを作成します。次に、行ごとにそれを解析し、その行にある情報でオブジェクトを変更します。別の "Processname:;ABC Buying" に到達したら、作業中のオブジェクトを結果リストに保存し、新しいオブジェクトを作成します。

あなたの質問には、行の解析方法などについて詳しく説明するのに十分な詳細がありませんが、上記は私が使用するアプローチであり、あなたがもっと良くなるとは思えません。ファイルを各オブジェクトに対応する行に分割するのではなく、途中でその分割を行うことを除いて、これはほとんどあなたが得たものであることに注意する価値があります。

于 2013-06-19T16:07:59.537 に答える
2

私がすることは、このデータを保持する厳密に型指定されたオブジェクトと、文字列を取得して個別の項目として分割するパーサーを用意することです。

// Has no behaviour - only properties 
public class Record 
{    
    public string ID { get;set;}    
    // Other fields 
}

// ------------------

// Only has methods ... 
public class RecordParser 
{    
   private string content;    

   public RecordParser(string content)    
   {
      this.content = content;    
   }

   public IEnumerable<Record> SplitRecords()    
   {
      var list = new List<Record>();

      foreach(string section in this.content.Split(/* ... */))
      {
          var record = CreateRecordFromSection(section);

          list.Add(record);
      }

      return list;    
   }

   private static Record CreateRecordFromSection(string content)     
   {
      StringBuilder currentText = new StringBuilder(content);

      var record = new Record()
      {
          ID = SetId(currentText),
          ProcessName = SetProcessName(currentText),
          /* Set other properties **/
      };

      return record;    
   }

   /* Methods for specific behaviour **/
   /* Modify the StringBuilder after you have trimmed the content required from it */    
   private static string SetProcessName(StringBuilder content) { }    
   private static int SetID(StringBuilder content) { }

   /** Others **/ 
}

Clean Codeを読んでから、ボブおじさんは別のアプローチを提供するかもしれません。

このアプローチでは、メソッドの内外で変数を渡すよりもローカル変数を優先します。この背後にある考え方は、クラスが内部でデータをどれだけ移動しているかをすぐに理解することです。あまりにも多くの変数を宣言している場合は、あまりにも多くのことが起こっていることを示しています。また、長い方法よりも短い方法を好みます。

public class RecordParser
{
   private List<Record> records;
   private Record currentRecord;
   private string allContent;
   private string currentSection;

   public RecordParser(string content)
   {
      this.allContent = content;
   }

   public IEnumerable<Record> Split()
   {
      records = new List<Record>();

      foreach(string section in GetSections())
      {
          this.currentSection = section;
          this.currentRecord = new Record();

          ParseSection();

          records.Add(currentRecord);
      }

      return records;
   }

   private IEnumerable<string> GetSections()
   {
      // Split allContent as needed and return the string sections
   }

   private void ParseSection()
   {
      ParseId();
      ParseProcessName();
   }

   private void ParseId()
   {
      int id = // Get ID from 'currentRecord'
      currentRecord.ID = id;
   }

   private void ParseProcessName()
   {
      string processName = // Get ProcessNamefrom 'currentRecord'
      currentRecord.ProcessName = processName;
   }

   /** Add methods with no parameters and use local variables
}

このアプローチは、変数の入出力を行わないため、慣れるまでに時間がかかる場合がありますが、非常にうまく流れます。

于 2013-06-19T16:38:14.263 に答える