C# 统计文本文件中相同字符串的数量

3
我有一个 foreach 语句,我遍历了来自文本文件的几行,在这里我已经将需要的行修剪并排序。我想做的是计算相同字符串出现的次数。怎么做呢?以下是我的代码,我卡在第二个 if 语句:
        foreach (string line in lines.Where(l => l.Length >= 5))
        {
            string a = line.Remove(0, 11);

            if ((a.Contains(mobName) && a.Contains("dies")))
            {

                mobDeathCount++;
            }
            if (a.Contains(mobName) && a.Contains("drops"))
            {
                string lastpart = a.Substring(a.LastIndexOf("drops"));
                string modifiedLastpart = lastpart.Remove(0, 6);

            }

以下是一些行的示例:

一袋硬币

一瓶西欧白兰地

一袋硬币

一袋硬币

卡斯盾牌

一张破旧的卷轴

所以我想要做的是计算有3行写着“一袋硬币”。但我需要让它可以适用于所有内容,有一个很大的下拉列表。所以不能把它们都加进去,那样会花费太长时间。

编辑

    private static void Main()
    {
        int mobDeathCount = 1;
        int lootCheckCount = 1;

        string[] lines =
            System.IO.File.ReadAllLines(@"C:\Users\Michael\Documents\Electronic Arts\Dark Age of Camelot\chat.log");
        Console.WriteLine(
            "Enter which mob you want to see, remember to include the, for an example; The siog seeker, remember to start with a capital T");
        string mobName = Console.ReadLine();


        foreach (string line in lines.Where(l => l.Length >= 5))
        {




            string a = line.Remove(0, 11);

            if ((a.Contains(mobName) && a.Contains("dies")))
            {

                mobDeathCount++;
            }
            if (a.Contains(mobName) && a.Contains("drops"))
            {
                string lastpart = a.Substring(a.LastIndexOf("drops"));
                string modifiedLastpart = lastpart.Remove(0, 6);

               var lineCountDict = modifiedLastpart.GroupBy(x => x).Where(x => x.Count() > 1).ToDictionary(x => x.Key, x => x.Count());
               foreach (var val in lineCountDict)
               {
                   Console.WriteLine(val.Key + " - " + val.Value);
               }

新的行;

[01:09:55] Siog搜寻者掉落了一袋硬币。

[01:09:55] Siog搜寻者掉落了一瓶Siog白兰地酒。

[01:09:55] Siog搜寻者死亡!

[01:09:55] 你获得了3,687,564点经验值。(1,638,917个营地奖励)

[01:10:31] 你施放了一个较小的解除附魔爆发法术!

[01:10:31] 你对Siog搜寻者造成了424 (+18)点伤害!

[01:10:31] Siog搜寻者掉落了一袋硬币。

[01:10:31] 你拾取了18个银和88个铜硬币。

[01:10:31] Siog搜寻者死亡。


1
第二个语句中的“stuck”是什么?从描述中,mobDropCount++就足够了 - 但我怀疑你对掉落物品的计数感兴趣,对吗?为此,请考虑使用 Dictionary<string,int>,其中掉落物品的名称是键,而次数是值。还可以在 SO 上搜索“C# 频率图”或“C# 直方图”,因为许多方法都会使用 Dictionary。 - user2246674
不,我的意思是如果有3行完全相同,我可以将它们计数。希望我表达得足够清楚 :) - Winkz
是的,有不同的掉落物品,但也有相同的掉落物品,我想计算特定掉落物品已经掉落了多少次。 - Winkz
你是如何获取行的?是逐行读取文件到字符串数组中吗? - Bearcat9425
使用字典有什么问题? - Marguth
显示剩余3条评论
5个回答

11

你可以使用LINQ来获取重复行的数量。这将创建一个包含字符串作为key以及该字符串出现次数作��value的字典。

var lineCountDict = lines.GroupBy(x => x).ToDictionary(x => x.Key, x => x.Count());

要读取值,只需遍历字典。因此,使用您的示例列表:

List<String> lines = new List<string>()
     { 
         "a bag of coins",
         "a siog brandy",
         "a bag of coins",
         "a bag of coins",
         "the Cath Shield",
         "a tattered scroll"
     };

var lineCountDict = lines.GroupBy(x => x).ToDictionary(x => x.Key, x => x.Count());

foreach (var val in lineCountDict)
{
     Console.WriteLine(val.Key + " - " + val.Value);
}

这将输出每个字符串及其出现的次数,包括那些只出现一次的字符串。如果你只想要重复的字符串,你可以通过添加Where子句来修改LINQ查询。

var lineCountDict = lines.GroupBy(x => x).Where(x => x.Count() > 1).ToDictionary(x => x.Key, x => x.Count());
字典将只有一个来自您示例中列表的项(一袋硬币),键将是一袋硬币,值将是3,因为它出现了3次。

基于评论的更新

这应该适用于您的情况

List<string> modifiedList = new List<string>();
int numberOfDrops = 0;

foreach (string line in lines.Where(l => l.Length >= 5))
{
     string ad = line.Remove(0, 11);

     if ((ad.Contains(mobName) && ad.Contains("dies")))
     {
        mobDeathCount++;
     }
     if (ad.Contains(mobName) && ad.Contains("drops"))
     {
         string lastpart = ad.Substring(ad.LastIndexOf("drops"));
         string modifiedLastpart = lastpart.Remove(0, 6);
         modifiedList.Add(modifiedLastpart);
         numberOfDrops++;
     }

}

double deathDropRatio = (double)mobDeathCount / (double)numberOfDrops;

var lineCountDict = modifiedList.GroupBy(x => x).Where(x => x.Count() > 1).ToDictionary(x => x.Key, x => x.Count());

foreach (var val in lineCountDict)
{
   Console.WriteLine(val.Key + " - " + val.Value);
}

1
当我尝试打印lineCountDict时,我得到了这个错误信息:System.Collection.Generic.Dictionary`2[System.String,System.Int32]。 - Winkz
2
你不能只是使用 lineCountDict.ToString();,因为它是一个字典结构。你可以通过它的键来访问它,例如 lineCountDict["可能重复的字符串"] 或者你可以循环遍历整个字典。在这里阅读有关字典的更多信息:http://www.dotnetperls.com/dictionary。你得到的不是错误消息,代码是正确的。 - keyboardP
1
这是因为这行代码 Console.WriteLine(val.Key + " - " + val.Value); 我刚刚添加了它来展示你如何访问每个键和每个值。你可以按照自己的喜好格式化它(或者你甚至不必将其打印出来)。 - keyboardP
1
由于项目名称是键,因此您只需执行 Console.WriteLine(lineCountDict["a bag of coins"]);,或者如果您想要 the Cath Shield 字符串的数量,您可以执行 Console.WriteLine(lineCountDict["the Cath Shield"]); - keyboardP
能不能自动完成呢?而且我还遇到了一个错误,无法将字符串转换为字符。 - Winkz
显示剩余25条评论

3
我喜欢使用字典来完成这个任务。
Dictionary<string, int> dict = new Dictionary<string, int>();
foreach (string s in yourStringList) {
    if (dict.ContainsKey(s)) {
        dict[s] = ++dict[s];
    } else {
        dict[s] = 1;
    }
}

你的字符串是字典的键,每个出现的次数都是值。
(免责声明:未进行代码测试;可能需要进行微调。)

尝试了你写的代码,但在foreach循环中出现了字符串s的错误,它说我无法将元素类型'char'转换为迭代器类型'string'。不确定这是什么意思。 - Winkz
这是在运行时还是编译时发生的? - James Cronen
我无法得到与您相同的确切错误,但我的猜测是,您要替换yourStringList的任何集合都返回单个字符而不是字符串。确保该列表确实由字符串组成。 - James Cronen
尝试了各种方法,但打印输出时得到的是以下内容:System.Collection.Generic.Dictionary`2[System.String,System.Int32] - Winkz
yourStringList 不应该是字典... 它应该是您想要计算唯一性的字符串列表。我没有在代码片段中包含该部分,因为似乎已经可以工作。如果您想一次只处理一个字符串,而不是作为列表处理所有字符串,则可以将 if (dict.ContainsKey(s)) { ... } else { ... } 部分提取出来,在每个要处理的字符串之后运行它。 - James Cronen

1
我认为这是您想要的:

我认为这就是您想要的:

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

foreach (string line in lines.Where(l => l.Length >= 5))
{
     string a = line.Remove(0, 11);

     if ((a.Contains(mobName) && a.Contains("dies")))
     {
         mobDeathCount++;
     }

     if (a.Contains(mobName) && a.Contains("drops"))
     {
         string lastpart = a.Substring(a.LastIndexOf("drops"));
         string modifiedLastpart = lastpart.Remove(0, 6);

         if (dropsDict.ContainsKey(modifiedLastpart)) 
         {
             dropsDict[modifiedLastpart] = dropsDict[modifiedLastpart]++;
         } 
         else 
         {
             dropsDict[modifiedLastpart] = 1;
         }
     }
}

谢谢你的努力,虽然我不完全确定你的代码是如何工作的,但我已经输入了它,但好像不能像我想要的那样运行。你知道如何找出有多少相似的行吗? - Winkz
执行完代码后,您可以在字典中检查每个物品的掉落数量。因此,如果其中一个掉落物品是“大匕首”,您可以检查dropsDict ["Big Dagger"],这将为您提供已掉落的大匕首数量。但是,我相信modifiedLastpart不仅仅是物品名称,所以可能会更加复杂。 - Dodecapus
我已经做了这样的工作,使得我处理的行要么以a或the开头,然后是物品的名称。比如一个匕首或某个剑之类的。 - Winkz

0
如果您想查找所有行数组中匹配的字符串数量(例如,“string one”出现2次,“string two”出现4次),请在foreach之外创建一个字典,在foreach内部首先放置以下内容:
Dictionary<string, int> same = new Dictionary<string, int>();

foreach (string line in lines)
{
      if (same.ContainsKey(line))
          ++same[line];
      else
          same.Add(line, 1);

      //......
      //do your other stuff
}

每个重复的字符串都将在字典的值中更新(字典内记录了所有字符串及其出现次数),通过这种方式,您可以检查某个字符串出现的次数。

我该如何从这里获取输出? - Winkz

0
也许这可以帮助你,这是一段代码,可以计算集合中所有重复的字符串。你需要根据自己的需要进行修改,但希望你能明白要点。
   var allStrings = new  List<string>{"stringOne", "stringOne", "stringTwo", "stringOne", "stringThree", "stringTwo"};
   var allStringsGrouped = allStrings.GroupBy(i => i);
   foreach (var group in allStringsGrouped)
   {
       System.Diagnostics.Debug.WriteLine(group.Key +" occured " + group.Count() + " times");
   }

输出如下:

stringOne occured 3 times
stringTwo occured 2 times
stringThree occured 1 times

谢谢,但问题是,我要输入所有不同类型的行需要大量的时间。 - Winkz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接