У меня есть информация, которая выглядит так
No. ID DATE_EVENT TIME_EVENT EVENT CODE
102995 018159871 07/08/2014 09:01:57 9008 1111
20398 018159871 07/08/2014 09:01:58 1000 1402
105541 018159871 07/08/2014 09:01:58 9210 1111
63492 018253609 07/08/2014 09:54:26 9008 905
37552 018253609 07/08/2014 09:54:45 9008 1111
9627 018253609 07/08/2014 09:54:48 9210 1111
112700 018253609 07/08/2014 09:54:48 1000 1402
50555 018253609 07/08/2014 09:55:56 1000 1401
63634 018253609 07/08/2014 09:55:56 9210 1111
34551 018330948 07/08/2014 09:21:51 9008 905
47252 018330948 07/08/2014 09:22:15 9008 1111
3975 018330948 07/08/2014 09:22:17 1000 1402
24196 018330948 07/08/2014 09:22:17 9210 1111
111150 018342571 07/08/2014 09:40:08 9008 905
17119 018342571 07/08/2014 09:40:19 9008 1111
18658 018342571 07/08/2014 09:40:21 9210 1111
25654 018342571 07/08/2014 09:40:21 1000 1402
Как видите, информация сортируется по времени и идентификатору. То, что я хотел бы сделать, - подсчитать количество времени, затраченного на 9008 905
и 9008 1111
прежде чем перейти к следующему
и я читаю это так
#include <iostream>
#include <fstream>
#include <vector>
#include <sstream>
using namespace std;
vector<string> &SplitString(const string &s, char delim, vector<string> &elems)
{
stringstream ss(s);
string item;
while (getline(ss, item, delim))
{
elems.push_back(item);
}
return elems;
}
int main(int argc, const char * argv[])
{
ifstream CustJ("/Users/Rr/p/B/Sample 1.txt");
string str;
string elements;
CustJ.seekg(0, ios::end);
str.reserve(CustJ.tellg());
CustJ.seekg(0, ios::beg);
str.assign((istreambuf_iterator<char>(CustJ)),
istreambuf_iterator<char>());
if (str.length() > 0)
{
vector<string> lines;
SplitString(str, '\n', lines);
vector<vector<string> > LineElements;
for (auto it : lines)
{
vector<string> elementsInLine;
SplitString(it, ',', elementsInLine);
LineElements.push_back(elementsInLine);
}
//this displays each element in an organized fashion
//for each line
for (auto it : LineElements)
{
//for each element IN that line
for (auto i : it)
{
//if it is not the last element in the line, then insert comma
if (i != it.back())
std::cout << i << ',';
else
std::cout << i;//last element does not get a trailing comma
}
//the end of the line
std::cout << '\n';
}
}
else
{
std::cout << "File Is empty" << std::endl;
return 1;
}
system("PAUSE");
return 0;
}
Я не уверен, что это лучший способ подойти к этой проблеме.
Благодарю.
Вы переформулировали вопрос, который сделал его более понятным. На мой взгляд, код здесь не самый важный. Что вам нужно сделать, так это разложить всю задачу на работоспособные элементы, что сделает задачу разрешимой.
На языках, отличных от C++, может быть супер элегантный ответ на Perl, Python, Ruby. Я напишу ответ на С#, так как типичная инфраструктура (IDE) может вам помочь, а LINQ (языковой интегрированный запрос) - ваш друг в таких задачах.
Никаких гарантий правильности кода, так как слишком много частей ответа на ваш вопрос. Код не является надежным, так как он будет генерировать исключения во многих местах, если вход является неуместным и т.д. Вам нужно определить стратегию обработки ошибок. В любом случае вам может потребоваться переопределить это на другом языке.
Первый компонент - это вход из файла. В декларативной форме:
var lines = File
.ReadAllLines("input.txt", Encoding.Default)
.Skip(1);
Нам нужно будет рассчитать промежутки времени от смежных дат-времени, поэтому мы их соединим:
var event_tuples = lines
.Zip(lines.Skip(1), (start, end) => new { Start = start, End = end });
Затем мы можем структурировать данные для более четкого запроса:
var entries = event_tuples
.Select(x => {
var start_data = x.Start.ParseColumns();
var end_data = x.End.ParseColumns();
var duration = end_data.ToDateTime() - start_data.ToDateTime();
return new
{
No=start_data[0],
Id=start_data[1],
Duration = duration,
Event = start_data[4],
Code = start_data[5]
};
})
;
Здесь вы можете увидеть использование предыдущего структурированного вывода запроса: .Start
и .End
. Подробнее о ParseColumns
и ToDateTime
позже.
Теперь к вашему примеру запрос:
подсчитайте количество времени, затраченного на 9008 905 и 9008 1111 Сначала найдите соответствующие события
var query = entries
.Where(x => x.Event == "9008"
&& new[] { "905", "1111" }.Contains(x.Code))
;
Console.WriteLine("{0} events found",query.Count());
а затем вычислить общую продолжительность:
var total_duration = query
.Select(x=>x.Duration)
.Aggregate((a, b) => a + b);
Console.WriteLine("total duration: {0}", total_duration);
Как видите, здесь очень много проблем: ввод файлов, синтаксический анализ строк, синтаксический анализ времени, запрос, агрегирование. Каждый из них требует особой осторожности. То, что вы определенно не хотите делать, это потратить время на детали низкого уровня, такие как обработка конечной линии. Рассмотрите возможность работы с соответствующими инструментами на самом высоком уровне абстракции.
Вернемся к ParseColumns
и ToDateTime
. Я написал их как методы расширения, которые являются основой LINQ и помогают писать декларативный код, даже их использование может быть спекулятивным здесь. На других языках существуют другие механизмы, которые позволяли бы такую читаемость.
Пример: реализация конкретных задач здесь:
static class Extensions {
public static string[] ParseColumns(this String line)
{
return line.Split(new char[] { ' ' },
StringSplitOptions.RemoveEmptyEntries);
}
public static DateTime ToDateTime(this String[] line)
{
const string datetime_format = "dd/MM/yyyy H:mm:ss";
return DateTime.ParseExact(
line[2] + " " + line[3],
datetime_format,
CultureInfo.InvariantCulture
);
}
}
Это частично скрывает некоторые более уродливые части кода, которые "просто заставляют его работать" для этого примера. Если программное обеспечение, которое вы пишете, будет использоваться и впоследствии расширено, такие части найдут свой путь где-то еще в коде, предпочтительно, за абстракциями.
Если вы придерживаетесь C++, вы, вероятно, захотите взглянуть на cpplinq.
управляемый в реестре
Дополнительное чтение: Мартин Фаулер: сборник