使用C ++基于标准分类信息

时间:2014-08-22 10:24:07

标签: c++ binning

我的信息看起来像这样

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

如您所见,信息按时间和ID排序。我希望能够做的是计算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;
}

我不确定这是否是解决此问题的最佳方法。

感谢。

1 个答案:

答案 0 :(得分:0)

你已经重新阐述了这个问题,这使得它更容易理解。在我看来,代码并不是最重要的事情。你需要做的是将整个任务分解为可行的项目,这将使任务可以解决。

在C ++以外的语言中可能有一个超级优雅的答案 - 在Perl,Python,Ruby中。我将在C#中写一个答案,因为典型的基础结构(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。有关ParseColumnsToDateTime之后的更多内容。

现在查看您的示例:

  

计算9008 905&amp; 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);

如您所见,这里有很多问题:文件输入,解析字符串,日期时间解析,查询,聚合。每个都需要特别小心。你绝对不想做的是花时间在低级细节上,比如终端处理。请考虑使用highest sufficient level of abstraction上的相应工具。

返回ParseColumnsToDateTime。我把它们写成Extension Methods,这是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
        );
    }
}

这部分隐藏了一些代码中较为丑陋的部分,这些部分只是为了这个例子而“正常工作”。如果您正在编写的软件将被使用并稍后进行扩展,那么此类部件将在代码中的其他位置找到自己的方式,最好是behind abstractions

如果您坚持使用C ++,您可能需要查看cpplinq

rextester

运行

额外阅读:Martin Fowler: Collection Pipeline