2013-06-19 58 views
1

我有一個CSV文件,它看起來像下面,如何分析非結構化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個occurances,那麼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); 
} 

什麼是做到這一點的最好方法是什麼?

+0

這看起來不像CSV。 – Lloyd

+0

CSV是一個'相對'結構化的文檔,Microsoft Jet Engine將爲您完成這項工作。所以它絕對是自定義代碼時間! - 鏈接到RFC的RFC - http://tools.ietf.org/html/rfc4180 –

+1

它的價值我可能會形容爲「具有複雜結構的文本文件」,因爲它的結構,它只是沒有所有的行都是一樣的。如果其完全非結構化的代碼幾乎沒有機會。 ;-) – Chris

回答

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,Bob大叔可以提供另一種方法,這更符合你的喜好。

該方法優先於局部變量而不是方法中的變量傳入和傳出。這背後的想法是你很快意識到你的班級在內部移動數據的程度。如果您聲明的變量太多,則表明發生太多事情了。它也比較長的方法更喜歡更短的方法。

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 
} 

這種方法可能需要一段時間才能習慣,因爲您不會傳入和傳出變量,但它流動得非常好。

4

首先這看起來不像我的CSV文件。其次我只是一行一行閱讀整個文件。創建一個新的對象,當你得到像「Processname:; ABC Buying」這樣的行時,它看起來就像你的對象的第一行。然後爲每一行解析它,並用該行上的任何信息修改對象。當你到達另一個「過程名稱:ABC購買」,然後將你一直在使用的對象保存到結果列表中並創建新的對象。

你的問題沒有足夠的細節去探討如何解析線條等的更多細節,但以上是我將使用的方法,我懷疑你會變得更好。值得注意的是,除了將文件分割成與每個對象相對應的行,我都會在分離過程中進行分割,而不是將其分割成多行。