2011-12-20 18 views
15

mam miliony linii wytworzonych z danych aktualizowanych co drugi, który wygląda tak:Manipulowanie linie danych

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

W kolumnie po lewej stronie przedstawia czas (format hhmmss), a kolumna po prawej stronie jest dane który jest aktualizowany drugi po drugim. Jak widać, nie jest to jednak drugie sekundowanie i brakuje niektórych czasów (w tym przykładzie brak jest 10:45:04, 10:45:07, 10:45:08). Moim celem jest, aby dodać brakujące sekundy, a do korzystania z danych z poprzedniego sekundę, że brakuje sekund, tak:

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 

I nie chcą „-” w wyniku, po prostu umieścić tam, aby zaznaczyć dodane linie. Do tej pory starałem się to osiągnąć za pomocą StreamReadera i StreamWriter, ale wygląda na to, że nie dostaną tego, czego chcę. Jestem początkującym programistą i nowicjuszem w C#, więc gdybyś mógł wskazać mi właściwy kierunek, byłoby wspaniale. Naprawdę zastanawiam się, czy jest to możliwe nawet w języku C# ... Spędziłem dużo czasu na MSDN i tutaj na SO szuka rozwiązania tego, ale do tej pory nie znalazłem żadnego.

Edytuj: Wiersze znajdują się w pliku tekstowym i chcę zapisać nowo utworzone dane w nowym pliku tekstowym.

+0

zakładam linie są z pliku tekstowego i chcesz utworzyć nowe z brakujących wartości? – Strillo

+6

+1 za świetne (dobrze napisane, wyjaśnione i sformatowane) pierwsze pytanie. –

+0

Tak, przepraszam, powinienem uwzględnić to w moim pytaniu. Wiersze znajdują się w pliku tekstowym i chcę zapisać nowo utworzone dane w nowym pliku tekstowym. –

Odpowiedz

3

ok, tutaj jest cały mecz strzelanie, przetestowany i działa przeciw danych testowych:

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

Będąc raczej prostym pytaniem, moją pierwszą myślą jest to, że jest to rodzaj zadania domowego lub pytania o rozmowę kwalifikacyjną - niekoniecznie, ale warto się zastanowić przed zaksięgowaniem pełnej odpowiedzi kodu źródłowego na łatwy problem. –

+1

Hej Bill K, przyszło mi do głowy, ale było dobrze napisane i uprzejme, więc dla mnie to całkiem proste; ludzie publikują pytania, jeśli chcesz na nie odpowiedzieć, zrób to, jeśli chcesz zagłosować, zakwestionować, poklepać, a nawet zignorować, możesz zrobić wszystkie te rzeczy. Możesz również złożyć skargę, gdy ktoś inny na nie odpowie. Wolność, musisz ją kochać. –

+0

Tak, zgadzam się - Dlatego właśnie delikatnie przypominam ludziom, że dostarczając pełne kodu odpowiedzi na proste pytania, nie robisz nikomu oprócz siebie przysługi. –

1

tej pory jak wstawianie nowych zapisów między niektórymi z nich odchodzi, radzę czytać w pliku tekstowym na wydzielonych liniach , a następnie przechowuje je w postaci List. W ten sposób możesz użyć metody Insert(...) do wstawienia nowych linii. Stamtąd możesz zapisać linie z powrotem do pliku.

Podczas czytania linii można użyć jednej ze statycznych metod pomocniczych w klasie System.IO.File: ReadAllText i ReadAllLines.

Uwaga: Dodałem linki do dokumentacji MSDN dla każdej z metod i klas, o których wspomniałem, ponieważ powiedziałeś, że jesteś nowy w C# i programowaniu w ogóle.

+1

Istnieją miliony linii, nie jest pewne przechowywanie całego pliku w pamięci to dobry pomysł. –

+0

Jeśli czyta miliony linii, które mogą stać się trochę nieeleganckie, zwłaszcza, że ​​aby znaleźć brakującą linię, potrzebuje tylko dwóch linii w pamięci naraz - Current and Previous. – asawyer

+0

Oba komentarze są prawdziwe - to tylko sugestia. Każde rzeczywiste rozwiązanie będzie całkowicie zależało od implementacji. Tak czy inaczej, odpowiedź powinna skłonić go do znalezienia rozwiązania, które mu odpowiada. –

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; 
} 

Oto kilka pseudo-kodu, aby rozpocząć. Myślę, że będziesz chciał tego typu algorytmu.

+0

Dzięki za napisanie tego, przeczytam o tym, co robi i zobaczę, czy to działa. –

4

Jest kilka rzeczy, które musisz ze sobą połączyć.

  1. Czytaj linię linia po pliku: Zobacz tutaj: Reading a Text File One Line at a Time
  2. Pisanie linię po linii pliku: StreamWriter.WriteLine
  3. Śledzić ostatniej linii odczytu. (Po prostu użyj zmiennej w pętli while, w której czytasz linie)
  4. Sprawdź, czy jest przerwa. Być może przez parsowanie pierwszej kolumny (string.Split) przy użyciu TimeSpan.Parse. Jeśli jest luka, napisz ostatnią linię odczytu, zwiększając przedział czasowy.
+0

Dzięki, myślę, że mam chwyt 1-3, ale # 4 jest dla mnie całkowicie nowy, więc zacznę czytać o string.Split i TimeSpan.Parse. –

3

W adition do wszystkich odpowiedzi, biorąc pod uwagę, że mówimy o dużych plików, należy rozważyć użycie MemoryMappedFiles można odczytać here aby zobaczyć, jak z nich korzystać z C#.

To jest nie wydajność poprawę, ale poprawa pamięci definetely is.

1

Zakłada się, że czasy nie są nigdy dłuższe niż dwie sekundy od siebie. Jeśli to założenie jest błędne, wystarczy zmodyfikować poniższy zapis, aby zapisać ostatnią wartość w pętli dla każdej brakującej sekundy. Aktualizacja Brakowało mi w twoim przykładzie, że w rzeczywistości może minąć kilka sekund. Zmieniłem poniższy przykład, aby to rozwiązać.

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

Wielkie dzięki! Przeczytam o tym, co to robi, a następnie wypróbuję. –

1

Oto trochę trudny kod dla Ciebie. Nie oddaję wszystkiego we właściwy sposób, tylko na początek.

 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); 
      } 
     } 
Powiązane problemy