Манипулирование строками данных
У меня есть миллионы строк, генерируемых из данных, обновляемых каждую секунду, которые выглядят следующим образом:
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, но похоже, что они не получат от меня то, что я хочу. Я новичок программист и новичок на С#, поэтому, если бы вы могли просто указать мне в правильном направлении, это было бы здорово. Мне действительно интересно, возможно ли это сделать на С#... Я потратил много времени на MSDN и здесь, на SO, ища решение для этого, но пока их не нашли.
Изменить: строки находятся в текстовом файле, и я хочу сохранить вновь созданные данные в новом текстовом файле.
Ответы
Ответ 1
ok, вот весь матч по стрельбе, протестирован и работает против ваших тестовых данных:
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); }
}
}
Ответ 2
Есть несколько вещей, которые вам нужно собрать.
Ответ 3
В соответствии со всеми ответами, учитывая, что вы говорите об огромных файлах, рассмотрите возможность использования MemoryMappedFiles, можете прочитать здесь, чтобы узнать, как использовать их с С#.
Это не улучшение производительности, но улучшение памяти, безусловно, есть.
Ответ 4
Пока добавляются новые записи между некоторыми из них, я бы посоветовал читать в текстовом файле в отдельные строки, а затем хранить их в List
. Таким образом, вы можете использовать метод Insert(...)
, чтобы вставить новые строки. Оттуда вы можете записать строки обратно в файл.
При чтении строк вы можете использовать любой из статических вспомогательных методов в классе System.IO.File
: ReadAllText
и ReadAllLines
.
Примечание. Я добавил ссылки на документацию MSDN для каждого из методов и классов, о которых я упоминал, поскольку вы сказали, что новичок в С# и вообще в программировании.
Ответ 5
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;
}
Вот какой-то псевдокод, чтобы вы начали. Я думаю, вам понадобится этот тип алгоритма.
Ответ 6
Это предполагает, что времена не более чем на две секунды. Если это предположение неверно, достаточно легко изменить нижеследующее, чтобы он записывал 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;
}
}
Ответ 7
Вот вам примерный код. Я не правильно распоряжаюсь всем, это просто, чтобы вы начали.
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;
}
Ответ 8
string line;//The line that is read.
string previousLine = "0 0";
int prevTime = 0;
//These "using" 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);
}
}