【问题标题】:Count how many time each Char is in a txt file in C#计算每个字符在 C# txt 文件中出现的次数
【发布时间】:2021-11-03 01:48:24
【问题描述】:

我有一个学校作业,我需要计算每个字母在 txt 文件中出现的次数,我不能使用字典,也不能使用 LinQ,然后我需要按字母顺序排列,并且按迭代顺序。

string = "hello world"
output = 

D=1
E=1
H=1
L=3
O=2
R=1
W=1

L=3
O=2
D=1
E=1
H=1
R=1
W=1

到目前为止,我所拥有的仅适用于按字母顺序排列,而不适用于迭代。

public void testChar() {

  string text = File.ReadAllText(@ "C:\Ecole\Session 2\Prog\Bloc 4\test.txt")?.ToUpper();

  text = Regex.Replace(text, @ "[^a-zA-Z]", "");

  List < char > listChar = new List < char > ();

  foreach(char lettre in text) {
    listChar.Add(lettre);
  }

  int countPosition = 0;

  List < int > position = new List < int > ();

  listOfChar.Add(listChar[0]);

  listOfRepetitions.Add(1);

  position.Add(countPosition);

  int jumpFirstItteration = 0;

  foreach(var item in listChar) {
    if (jumpFirstItteration == 0) {
      jumpFirstItteration++;

    }

    if (listOfChar.Contains(item)) {
      int pos = listOfChar.IndexOf(item);
      listOfRepetitions[pos] += 1;

    } else if (!listOfChar.Contains(item)) {

      listOfChar.Add(item);
      listOfRepetitions.Add(1);

      countPosition++;
      position.Add(countPosition);

    }

  }

}

请帮忙:D

【问题讨论】:

  • 他们可能希望您使用一个int 数组,其中索引 0 = a、1 = b 等等,并在您看到字符时增加数组的索引。就像var count = new int[26]; foreach(var c in text) count[c - 'a']++; 一样,假设所有字符都是 a 到 b 小写。
  • @juharr 按字母顺序对这种数组进行排序并保留频率会变得很棘手
  • @Sébastien 它已经按字母顺序排列。它只需要一些工作来对频率进行排序,同时跟踪原始索引以获得第二次排序。
  • @juharr,对,我的意思是“按频率顺序排序很棘手”,而不是“按字母顺序”。

标签: c# list sorting dictionary


【解决方案1】:

也许添加类似排序功能的东西?

    position.sort((a,b)=> if a < b return 1; if a > b return -1; return 0;)

这将是一个基于整数值的普通排序函数,但由于您正在处理一个列表,它变得有点乏味。

    position.sort((a,b)=> if a[i] < b[i] return 1; if a[i] > b[i] return -1; return 0;)

其中 i 要比较的整数的索引

原谅我,我来自java

【讨论】:

  • 这不太可能被接受为家庭作业解决方案:)
  • 在 C# 中,List&lt;T&gt;.Sort(Comparison&lt;T&gt;) 接受一个排序委托,其作用相同。
【解决方案2】:

一些可能会派上用场的建议。
您可以构建一个容器(此处为 CharInfo 类对象),以存储您收集的有关找到的每个字符的信息、它的位置以及在文本文件中找到它的次数。
此类 container 实现了IComparable(比较器只是将 char 值作为一个整数与另一个对象的 char 值进行测试)。
这使得对List&lt;CharInfo&gt; 的排序变得简单,只需调用它的Sort() 方法:

public class CharInfo : IComparable<CharInfo>
{
    public CharInfo(char letter, int position) {
        Character = letter;
        Positions.Add(position);
    }

    public char Character { get; }
    public int Occurrences { get; set; } = 1;
    public List<int> Positions { get; } = new List<int>();
    public static List<string> CharsFound { get; } = new List<string>();

    public int CompareTo(CharInfo other) => this.Character - other.Character;
}

由于您可以使用 Regex 类,因此您可以使用 Regex.Matches() 返回 [a-zA-Z] 范围内的所有字符。每个Match 对象还存储找到该角色的位置。

(请注意,您可以只使用 Matches 集合和 GroupBy() 结果,但您不能使用 LINQ 并且其他 建议 将只是静音 :)

循环Matches 集合,测试列表中是否已经存在一个字符;如果是,请将1 添加到Occurrences 属性并将其位置添加到Positions 属性。
如果它不存在,请将新的 CharInfo 对象添加到集合中。类的构造函数接受一个字符和一个位置。 Occurrences 属性默认为 1(您创建了一个新的 CharInfo,因为您找到了一个新字符,即出现次数为 1)。

最后,只需 Sort() 使用其自定义比较器的集合。 Sort() 会调用类的IComparable.CompareTo() 方法。

string text = File.ReadAllText([File Path]);
var matches = Regex.Matches(text, @"[a-zA-Z]", RegexOptions.Multiline);
var charsInfo = new List<CharInfo>();
        
foreach (Match m in matches) {
    int pos = CharInfo.CharsFound.IndexOf(m.Value);
    if (pos >= 0) {
        charsInfo[pos].Occurrences += 1;
        charsInfo[pos].Positions.Add(m.Index);
    }
    else {
        CharInfo.CharsFound.Add(m.Value);
        charsInfo.Add(new CharInfo(m.Value[0], m.Index));
    }
}

// Sort the List<CharInfo> using the provided comparer
charsInfo.Sort();
  • 在搜索其他文本文件之前,请致电 CharInfo.CharsFound.Clear()

然后您可以将结果打印为:

foreach (var chInfo in charsInfo) {
    Console.WriteLine(
        $"Char: {chInfo.Character} " +
        $"Occurrences: {chInfo.Occurrences} " +
        $"Positions: {string.Join(",", chInfo.Positions)}");
}

请注意,大写和小写字符被视为不同的元素。
根据需要修改。

【讨论】:

    【解决方案3】:

    不一定是最有效的,但很有效。

    首先,一个简单的字母类可以比较它的属性。

     public class Letter
        {
            public char Symbole { get; set; }
            public int Frequency { get; set; }
       
    
            public int CompareLetter(object obj)
            {
                Letter other = obj as Letter;
                
                if (other == null) return 1;
                
                return this.Symbole.CompareTo(other.Symbole);
            }
            public int CompareFrequency(object obj)
            {
                Letter other = obj as Letter;
    
                if (other == null) return 1;
    
                return this.Frequency.CompareTo(other.Frequency);
            }
        }
    

    然后是填充字母列表的方法

     public static List<Letter> ReturnLettersCount(string fileName)
            {
                string text;
                List<Letter> letters = new List<Letter>();
    
                using (StreamReader sr = new StreamReader(fileName))
                {
                    text = sr.ReadToEnd().ToLower();
                }
    
                foreach (char c in text)
                {
                    if (char.IsLetter(c))
                    {
                        Letter letter = letters.Find((letter) => letter.Symbole == c);
                        if (letter == null)
                        {
                            letters.Add(new Letter() { Symbole = c, Frequency = 1 });
                        }
                        else
                        {
                            letter.Frequency++;
                        }
                    }
                }
    
                return letters;
            }
    

    还有一个用户代码

    static void Main(string[] args)
            {
                string fileName = @"path\to\your\textFile.txt";
    
                List<Letter> letters = ReturnLettersCount(fileName);
    
                letters.Sort( (a, b) => a.CompareLetter(b) );
                foreach(Letter letter in letters)
                {
                    Console.WriteLine($"{letter.Symbole}: {letter.Frequency}");
                }
    
                Console.WriteLine("--------------------------");
                
                letters.Sort((b, a) => a.CompareFrequency(b));
                foreach (Letter letter in letters)
                {
                    Console.WriteLine($"{letter.Symbole}: {letter.Frequency}");
                }
    
            }
    

    【讨论】:

      【解决方案4】:

      没有 Linq 和 Dictionary,就像步行到 20 英里外的杂货店一样。 ;)

      //C# vs >= 8.0
      using System;
      using Entry = System.Collections.Generic.KeyValuePair<char, int>;    
      
         // class header ...
          public static void Sort() {
      
              var text    = "hello world";
              var contest = new System.Collections.Generic.List<Entry>(text.Length);
      
              foreach (var c in text) {
                  if (!char.IsLetter(c)) {
                      continue;
                  }
                  var i = contest.FindIndex(kv => kv.Key == c);
                  if (i < 0) {
                      contest.Add(new(c, 1));
                  }
                  else {
                      contest[i] = new(c, contest[i].Value + 1);
                  }
              }
      
              contest.Sort((e1, e2) => e1.Key - e2.Key);
              Console.Write("\n{0}\n\n", string.Join('\n', contest));
      
              contest.Sort((e1, e2) => e2.Value - e1.Value);
              Console.Write("\n{0}\n\n", string.Join('\n', contest));
          }
        
      

      【讨论】:

        【解决方案5】:

        计算一致性的规范方法是使用整数数组来计算字母,其大小与文本中不同字母的数量相同 - 在这种情况下,只是正常的大写字母字符 AZ。

        然后遍历大写字母,如果它们在范围内,则增加与该字母对应的计数。

        为了简化这一点,你可以做两个观察:

        • 要将字母转换为索引,只需从字符代码中减去“A”即可。
        • 要将索引转换为字母,只需将“A”添加到索引并将结果转换回字符即可。 (强制转换是必要的,因为将 int 添加到 char 的结果是 int,而不是 char。)

        完成此操作后,您将获得按字母顺序排列的所有字符数。但是,您还需要按出现频率排序的字母。要计算它,您可以使用an overload of Array.Sort() that takes two arrays:第一个参数是要排序的数组,第二个参数是要排序的数组,与第一个数组一样。

        如果您将计数数组作为第一个数组传递,并且将按字母顺序计数的所有字母的数组作为第二个数组(即字母 A..Z),那么在对第二个数组进行排序后会给您以正确顺序显示的字母与第一个数组一起显示。

        将所有这些放在一起:

        public void testChar()
        {
            string filename    = @"C:\Ecole\Session 2\Prog\Bloc 4\test.txt";
            string text        = File.ReadAllText(filename).ToUpper();
            int[]  concordance = new int[26]; // 26 different letters of the alphabet to count.
        
            foreach (char c in text)
            {
                int index = c - 'A';  // A..Z will convert to 0..25; other chars will be outside that range.
        
                if (index >= 0 && index < 26)
                    ++concordance[index];
            }
        
            // Display frequency in alphabetic order, omitting chars with 0 occurances.
            for (int i = 0; i < concordance.Length; ++i)
            {
                if (concordance[i] > 0)
                    Console.WriteLine($"{(char)('A'+i)} = {concordance[i]}");
            }
        
            Console.WriteLine();
        
            // For sorting by frequency we need another array of chars A..Z in alphabetical order.
            char[] aToZ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
            Array.Sort(concordance, aToZ);
        
            // Display frequency in occurance order, omitting chars with 0 occurances.
            for (int i = 0; i < concordance.Length; ++i)
            {
                if (concordance[i] > 0)
                    Console.WriteLine($"{aToZ[i]} = {concordance[i]}");
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2021-03-16
          • 1970-01-01
          • 1970-01-01
          • 2022-10-15
          • 2017-10-28
          • 1970-01-01
          • 2020-07-23
          • 2016-01-23
          • 1970-01-01
          相关资源
          最近更新 更多