2011-12-20 44 views
15

我有百萬從每秒更新一次數據生成線中的哪看起來像這樣:操作數據的線

104500 4783 
104501 8930 
104502 21794 
104503 21927 
104505 5746 
104506 9968 
104509 5867 
104510 46353 
104511 7767 
104512 4903 

在左邊的列表示時間(HHMMSS格式),和右邊的列是數據這是逐秒更新的。但是,正如你所看到的,它並不是真正的秒針,還有一些缺失的時間(在這個例子中缺少10:45:04,10:45:07,10:45:08)。我的目標是在缺少秒添加,並從以前的第二使用數據失蹤第二,像這樣:

104500 4783 
104501 8930 
104502 21794 
104503 21927 
104504 21927 -- 
104505 5746 
104506 9968 
104507 9968 -- 
104508 9968 -- 
104509 5867 
104510 46353 
104511 7767 
104512 4903 

我不希望「 - 」的結果,我只是把那些標記爲添加的行。到目前爲止,我已經嘗試使用StreamReader和StreamWriter完成此操作,但似乎並不像他們想要的那樣得到我想要的。我是一個新手程序員,也是C#的新手,所以如果你能指出我正確的方向,那會很棒。我真的只是想知道這是否甚至可以在C#中完成......我在MSDN上花了很多時間,在這裏尋找解決方案,但到目前爲止還沒有找到。

編輯:行是一個文本文件,我想新創建的數據存儲在一個新的文本文件。

+0

我假設線是從一個文本文件,你想創建一個新的包含遺漏值? – Strillo 2011-12-20 16:48:56

+6

對於一個偉大的(寫得很好,解釋和格式化)的第一個問題+1。 – 2011-12-20 16:49:29

+0

是的,對不起,我應該在我的問題中包括這個。這些行在一個文本文件中,我想將新創建的數據存儲在一個新的文本文件中。 – 2011-12-20 16:50:07

回答

3

好吧,這裏是整個射擊比賽,測試,對測試數據的工作:

public void InjectMissingData() 
{ 
    DataLine lastDataLine = null; 
    using (var writer = new StreamWriter(File.Create("c:\\temp\\out.txt"))) 
    { 
     using (var reader = new StreamReader("c:\\temp\\in.txt")) 
     { 
      while (!reader.EndOfStream) 
      { 
       var dataLine = DataLine.Parse(reader.ReadLine()); 

       while (lastDataLine != null && dataLine.Occurence - lastDataLine.Occurence > TimeSpan.FromSeconds(1)) 
       { 
        lastDataLine = new DataLine(lastDataLine.Occurence + TimeSpan.FromSeconds(1), lastDataLine.Data); 
        writer.WriteLine(lastDataLine.Line); 
       } 

       writer.WriteLine(dataLine.Line); 

       lastDataLine = dataLine; 
      } 
     } 
    } 
} 

public class DataLine 
{ 
    public static DataLine Parse(string line) 
    { 
     var timeString = string.Format("{0}:{1}:{2}", line.Substring(0, 2), line.Substring(2, 2), 
             line.Substring(4, 2)); 

     return new DataLine(TimeSpan.Parse(timeString), long.Parse(line.Substring(7, line.Length - 7).Trim())); 
    } 

    public DataLine(TimeSpan occurence, long data) 
    { 
     Occurence = occurence; 
     Data = data; 
    } 

    public TimeSpan Occurence { get; private set; } 
    public long Data { get; private set; } 

    public string Line 
    { 
     get { return string.Format("{0}{1}{2} {3}", 
      Occurence.Hours.ToString().PadLeft(2, Char.Parse("0")), 
      Occurence.Minutes.ToString().PadLeft(2, Char.Parse("0")), 
      Occurence.Seconds.ToString().PadLeft(2, Char.Parse("0")), 
      Data); } 
    } 
} 
+0

作爲一個相當簡單的問題,我的第一個想法是,這是一些作業或面試問題 - 不一定,但在發佈完整源代碼答案之前,值得考慮一個簡單的問題。 – 2011-12-20 19:30:02

+1

嘿,比爾K,它出現在我身上,但它寫得很好,很禮貌,所以對我來說很簡單;人們會發布問題,如果您想回答問題,請這樣做,如果您想對其投票,質疑,嘲笑它甚至忽略它,則可以執行所有這些操作。你也可以在別人回答時抱怨。自由,你必須愛它。 – 2011-12-20 20:21:10

+0

是的,我同意 - 因此,我只是輕輕地提醒人們,如果您對自己可能做的簡單問題提供代碼完整的答案,但只有您自己可以幫忙。 – 2011-12-20 20:25:24

1

至於某幾個推移,我會建議閱讀的文本文件導入分隔行之間插入新的條目,然後將它們存儲在List中。這樣,您可以使用Insert(...)方法來插入新行。從那裏,你可以將這些行寫回到文件中。

當讀線,您可以使用在System.IO.File類中的靜態輔助方法:ReadAllTextReadAllLines

注意:我已經爲我提到的每個方法和類添加了MSDN文檔的鏈接,因爲您說您是C#和編程的新手。

+1

有數百萬行,不確定將整個文件存儲在內存中是一個好主意。 – 2011-12-20 16:55:35

+0

如果他讀數以百萬計的行可能會有一些unweildy,尤其是因爲找到一條缺失的行,他一次只需要兩行內存 - Current和Previous。 – asawyer 2011-12-20 16:55:46

+0

這兩條評論都是真實的 - 這只是一個建議。任何實際的解決方案都完全依賴於實施。無論哪種方式,答案都會導致他找到適合他的解決方案。 – 2011-12-20 16:57:30

1
String prevTime; 
String prevData; 

while(String line = myStreamReader.ReadLine()) 
{ 
    String[] parts = line.Split(new Char[] { ' ' }); 
    String time = parts[0]; 
    String data = parts[1]; 

    Int32 iPrevTime = Int32.Parse(prevTime); 
    Int32 iCurrentTime = Int32.Parse(time); 

    // May need to loop here if you're missing more than one second 
    if(iCurrentTime > iPrevTime + 1) 
      AddData((iPrevTime + 1).ToString(), prevData); 

    AddData(time, data); 
    prevTime = time; 
    prevData = data; 
} 

下面是一些讓你開始的僞代碼。我想你會想要這種類型的算法。

+0

感謝您寫這篇文章,我會閱讀它的作用,看看它是否有用。 – 2011-12-20 17:07:56

4

有幾件事情需要放在一起。

  1. 閱讀文件中的行由行:在這裏看到:Reading a Text File One Line at a Time
  2. 寫一個文件中的行由行:StreamWriter.WriteLine
  3. 跟蹤最後一次讀線。 (只需在你的while循環中使用一個變量,你可以在這裏讀取行)
  4. 檢查是否有差距。也許通過使用TimeSpan.Parse解析第一列(string.Split)。如果存在間隙,則寫入最後一條讀取線,增加時間跨度。
+0

謝謝,我認爲我對1-3有一個把握,但#4對我來說是全新的,所以我將開始閱讀有關string.Split和TimeSpan.Parse的內容。 – 2011-12-20 17:05:46

3

在ADITION所有答案,考慮到你是在談論一個巨大的文件,請考慮使用MemoryMappedFiles,可以閱讀here,看看如何在C#中使用它們。

這是不是性能改善,但改善記憶 definetely是。

1

這假設時間不會超過一秒。如果這種假設是錯誤的,很容易修改下面的內容,因此它將lastValue寫入循環中,每秒丟失一次。 更新我錯過了你的例子,它實際上可能會錯過幾秒鐘。我改變了下面的例子來解決這個問題。

using (StreamReader reader = OpenYourInputFile()) 
using (StreamWriter writer = OpenYourOutputFile()) 
{ 
    TimeSpan? lastTime; 
    TimeSpan currentTime, maxDiff = TimeSpan.FromSeconds(1); 
    string lastValue, currentline, currentValue, format = "{0:hhmmss} {1}"; 

    while((currentLine = reader.ReadLine()) != null) 
    { 
     string[] s = currentLine.Split(' '); 
     currentTime = DateTime.ParseExact("hhmmss", s[0] CultureInfo.InvariantCulture).TimeOfDay; 
     currentValue = s[1]; 

     if (lastTime.HasValue && currentTime - lastTime.Value > maxDiff) 
     { 
     for(int x = 1; x <= (currentTime - lastTime).Seconds; x++) writer.WriteLine(string.Format(format, DateTime.Today.Add(lastTime).AddSeconds(x), lastValue); 
     } 

     writer.WriteLine(string.Format(format, DateTime.Today.Add(currentTime), currentValue); 

     lastTime = currentTime; 
     lastValue = currentValue; 
    } 

} 
+0

非常感謝!我會閱讀這些信息,然後嘗試。 – 2011-12-20 17:10:53

1

這裏有一些粗略的代碼給你。我沒有妥善處理所有事情,只是爲了讓你開始。

 DateTime lastTime; 
     string lastValue = null; 
     StreamReader reader = File.OpenText("path"); 
     StreamWriter writer = new StreamWriter(File.OpenWrite("newPath")); 

     while (!reader.EndOfStream) 
     { 
      string[] lineData = reader.ReadLine().Split(' '); 
      DateTime currentTime = DateTime.Parse(lineData[0]); 
      string value = lineData[1]; 

      if (lastValue != null) 
      { 
       while (lastTime < currentTime.AddSeconds(-1)) 
       { 
        lastTime = lastTime.AddSeconds(1); 
        writer.WriteLine("{0} {1}", lastTime, lastValue); 
       } 
      } 
      writer.WriteLine("{0} {1}", currentTime, value); 
      lastTime = currentTime; 
      lastValue = value; 
     } 
1
 string line;//The line that is read. 
     string previousLine = "0 0"; 
     int prevTime = 0; 

     //These "using"'s are so that the resources they use will be freed when the block (i.e. {}) is finished. 
     using (System.IO.StreamReader originalFile = new System.IO.StreamReader("c:\\users\\Me\\t.txt")) 
     using (System.IO.StreamWriter newFile = new System.IO.StreamWriter("c:\\users\\Me\\t2.txt")) 
     { 
      while ((line = originalFile.ReadLine()) != null) 
      { 
       //"Split" changes the words in "line" (- that are separated by a space) to an array. 
       //"Parse" takes the first in that array (by using "[0]") and changes it into an integer. 
       int time = int.Parse(line.Split(' ')[0]); 
       while (prevTime != 0 && time > ++prevTime) newFile.WriteLine(prevTime.ToString() + " " + previousLine.Split(' ')[1]); 

       previousLine = line; 
       prevTime = time; 
       newFile.WriteLine(line); 
      } 
     }