3

好的,所以标题并不是最好的。但这是我的问题:

我制作了一个小程序,当外部程序发生某些事件时,它会将一些名称和一个整数写入 .txt 文档。

问题是一个名字可以出现在文档的几行中,所以我想总结每个特定人的整数,以便我得到他/她的总分,然后对其进行排序。

例如:

原行:

Aaaa Aaa 5
Bbbb Bbb 7
Cccc Ccc 2
Aaaa Aaa 4
Cccc Ccc 4
Bbbb Bbb 1
Dddd Ddd 1

我想要的输出:

1. Aaaa Aaa 9
2. Bbbb Bbb 8
3. Cccc Ccc 6
4. Dddd Ddd 1

有没有办法在 C# 中做到这一点?我试图读入文件中的每一行并搜索一个人的名字。但这并没有真正的帮助,我不知道如何解决这个问题。有什么建议吗?

4

7 回答 7

3

显然,文本有点像你用来总结数字的键,那么为什么不Dictionary<string, int>先用 a 来总结,然后再写呢?

例子:

Dictionary<string, int> sums = new Dictionary<string, int>();

...

if (sums.ContainsKey(theNewString))
    sums[theNewString] += theNewNumber;
else
    sums[theNewString] = theNewNumber;

当你知道你完成了,写文件。您也可以在每次更新字典后重新写入文件,但请记住,如果您不清除它,字典会不断增长。

另外:如果程序重新启动,这将不起作用,除非您在每次程序启动时创建一个新文件。否则,当程序开始继续总结时,您必须将现有文件读入字典。

于 2013-06-13T10:21:49.157 回答
2
lines.GroupBy(line => string.Join(" ", line.Split().Take(2)))
     .Select((g, index) => 
           string.Format("{0}. {1} {2}", 
            index, 
            g.Key, 
            g.Sum(line => int.Parse(line.Split().Last()))));
于 2013-06-13T10:34:14.397 回答
2

此 Linq 查询将所需结果返回为IEnumerable<string>

IEnumerable<string> lineGroups = File.ReadLines(path)
.Select((l, i) => new { Line = l, Parts = l.Split() })
.Select(x => new
{
    Number = x.Parts.ElementAtOrDefault(2).TryGetInt() ?? 1,
    Col1 = x.Parts.ElementAtOrDefault(0),
    Col2 = x.Parts.ElementAtOrDefault(1),
    x.Line,
    x.Parts
})
.GroupBy(x =>new { x.Col1, x.Col2 })
.Select((g, groupIndex) =>
    string.Format("{0}. {1} {2} {3}",
    groupIndex + 1, g.Key.Col1, g.Key.Col2, g.Sum(x => x.Number)));

输出:

foreach (var grp in lineGroups)
    Console.WriteLine(grp);

这是输出:

1. Aaaa Aaa 9
2. Bbbb Bbb 8
3. Cccc Ccc 2  // different than your desired ouput but seems to be correct
4. Dddd Ddd 1

这些是我在 Linq 查询中使用的扩展方法,用于尝试解析一个string常见的值类型int(如上)。如果它不可解析,则返回一个可为空的类型:

public static class NumericExtensions
{
    public static bool IsBetween(this int value, int fromInclusive, int toInclusive)
    {
        return value >= fromInclusive && value <= toInclusive;
    }

    public static Decimal? TryGetDecimal(this string item)
    {
        Decimal d;
        bool success = Decimal.TryParse(item, out d);
        return success ? (Decimal?)d : (Decimal?)null;
    }

    public static int? TryGetInt(this string item)
    {
        int i;
        bool success = int.TryParse(item, out i);
        return success ? (int?)i : (int?)null;
    }

    public static bool TryGetBool(this string item)
    {
        bool b = false;
        Boolean.TryParse(item, out b);
        return b; ;
    }

    public static Version TryGetVersion(this string item)
    {
        Version v;
        bool success = Version.TryParse(item, out v);
        return v;
    }
}
于 2013-06-13T10:34:20.840 回答
2

创建一个以键为名称的字典。作为字典中每个项目的值,使用整数并将其添加到(的值)已经存在的键(或不存在)。

于 2013-06-13T10:21:54.240 回答
1
var results = File.ReadAllLines("filename.txt")
    .Select(x => x.Split())
    .GroupBy(y => new { y1 = y[0], y2 = y[1] })
    .Select(g => new { g.Key.y1, g.Key.y2, Sum = g.Sum(v => int.Parse(v[2])) })
    .OrderByDescending(p => p.Sum)
    .Select(m => m.y1 + " " + m.y2 + " " + m.Sum).ToList();
于 2013-06-13T10:31:25.970 回答
1

类似的东西(不是那么优雅,但不依赖于空间的数量)

var lines = File.ReadAllLines(@"<pathToFile>");

var result = lines
                .Select(m => m.Split(' '))
                .Select(x => new {
                    text = string.Join(" ", x.Where(z => z != x.Last())),
                    val = Convert.ToInt32(x.Last())
                })
                .GroupBy(x => x.text)
                .Select(g => new {
                    text =g.Key,
                    sum = g.Sum(z => z.val)
                }).ToList();
于 2013-06-13T10:31:52.493 回答
1

使用 LINQ 和正则表达式的另一种解决方案(用于验证行格式并从中获取名称和值):

Regex regex = new Regex(@"^(?<name>.*)\s(?<value>\d+)$");

var query = from line in File.ReadLines(file_name)
            let match = regex.Match(line)
            where match.Success
            select new {
                Name = match.Groups["name"].Value,
                Value = match.Groups["value"].Value
            } into item
            group item by item.Name into g
            orderby g.Key
            select new {
                Name = g.Key,
                Total = g.Sum(x => Int32.Parse(x.Value))
            };

此处未验证值溢出。如果某些值可能大于 Int32.MaxValue,则将 sum 计算更改为

g.Sum(x => { int value; return Int32.TryParse(x.Value, out value) ? value : 0; })
于 2013-06-13T12:04:04.373 回答