C#教程

关注公众号 jb51net

关闭
首页 > 软件编程 > C#教程 > C#并发单词频率统计器

使用C#从零到完整实现的并发单词频率统计器

作者:freedom ⁠

在工业自动化中,经常需要处理日志文件、设备数据记录,并统计关键字出现频率,所以本文给大家使用C#从零到完整实现了一个并发单词频率统计器,需要的朋友可以参考下

一、项目背景与目标

在工业自动化中,经常需要处理日志文件、设备数据记录,并统计关键字出现频率。本项目的目标是:

通过这个项目,熟悉 C# 异步编程、LINQ、正则表达式、字典操作等核心技能。

二、核心代码与语法解析

2.1 异步创建文件(并发)

使用 Task.WhenAll 并发创建多个文件,提高效率。

private static async Task<List<string>> createTxt()
{
    // 确保目录存在
    if (!Directory.Exists(DEFAULT_PATH))
        Directory.CreateDirectory(DEFAULT_PATH);

    while (true)
    {
        Console.Write("Please input text type as you want, you can input more:>>>");
        string? input = Console.ReadLine();
        if (string.IsNullOrWhiteSpace(input)) continue;

        string[] filesName = input.Split(',', StringSplitOptions.RemoveEmptyEntries)
                                  .Select(s => s.Trim())
                                  .ToArray();
        var textList = new List<Task<string>>();

        foreach (string text in filesName)
        {
            if (!parityText(text)) continue;               // 文件名校验
            if (File.Exists(Path.Combine(DEFAULT_PATH, text)))
            {
                Console.WriteLine("File is Existing");
                continue;
            }
            textList.Add(createAsyncFile(text));           // 每个文件一个异步任务
        }

        if (textList.Count == 0) continue;

        string[] files = await Task.WhenAll(textList);     // 并发等待所有文件创建完成
        List<string> fileList = files.Where(f => f != null)
                                     .Select(f => Path.Combine(DEFAULT_PATH, f))
                                     .ToList();
        Console.WriteLine($"所有文件创建完成,共 {textList.Count} 个。");
        return fileList;
    }
}

private static async Task<string> createAsyncFile(string text)
{
    try
    {
        string default_content = $"// Created:{DateTime.Now:yyyy-MM-dd HH:mm:ss} \n// Author: Dong Wang  ";
        await File.WriteAllTextAsync(Path.Combine(DEFAULT_PATH, text), default_content);
        lock (_lock) Console.WriteLine($"Created successfully!!!! File Name:{text}");
        return text;
    }
    catch (Exception ex)
    {
        lock (_lock) Console.WriteLine($"{ex.Message}");
        return null;
    }
}

语法点:

2.2 正则分割单词(统计词频)

private static Dictionary<string, int> GetTextsFromFile(string fileReal)
{
    string text = File.ReadAllText(Path.Combine(DEFAULT_PATH, fileReal)).ToLower();
    // 按非单词字符分割(空格、标点、换行等)
    string[] words = Regex.Split(text, @"\W+");
    var wordCount = new Dictionary<string, int>();
    foreach (string w in words)
    {
        if (string.IsNullOrEmpty(w)) continue;
        string word = w.ToLower();
        if (wordCount.ContainsKey(word))
            wordCount[word]++;
        else
            wordCount[word] = 1;
    }
    return wordCount;
}

关键正则: \W+ 匹配一个或多个非单词字符,相当于分割符。

2.3 并发统计所有文件(使用元组返回文件名和结果)

var tasks = filesReal.Select(async filePath =>
{
    var wordCount = await Task.Run(() => GetTextsFromFile(filePath));
    return (FileName: Path.GetFileName(filePath), WordCount: wordCount);
});
var results = await Task.WhenAll(tasks);
var textWords = results.ToDictionary(r => r.FileName, r => r.WordCount);

2.4 忽略文件名大小写的字典

Dictionary<string, Dictionary<string, int>> textWords = 
    new Dictionary<string, Dictionary<string, int>>(StringComparer.OrdinalIgnoreCase);

这样用户输入 "HAHA.TXT""haha.txt" 都能匹配。

2.5 用户交互查询

private static void UserInputQueryWord(string v, Dictionary<string, Dictionary<string, int>> textwords)
{
    while (true)
    {
        Console.Write("Please input query word:>>");
        string? input = Console.ReadLine();
        if (input == null) continue;
        input = input.ToLower();
        string[] words = SplitWord(input);
        foreach (string word in words)
        {
            if (textwords.TryGetValue(v, out var wordCount) &&
                wordCount.TryGetValue(word, out int count))
            {
                Console.WriteLine($"该文件 {v} 中的搜索词 {word} 查询出现的频率: {count} 次");
            }
            else
            {
                Console.WriteLine($"该文件 {v} 中的搜索词 {word} 未找到");
            }
        }
        break;
    }
}

三、遇到的问题与解决方案

问题描述原因解决方案
正则分割后得到很多标点符号误用 \w+ 作为分隔符改为 \W+(大写 W)
并发输出控制台内容混乱多个线程同时调用 Console.WriteLine使用 lock 同步
文件名大小写导致查询失败默认字典区分大小写构造函数传入 StringComparer.OrdinalIgnoreCase
Task.WhenAll 返回数组无法直接转换成嵌套字典任务只返回字典,丢失文件名任务返回元组 (FileName, WordCount)
并发统计时重复创建任务在循环内执行 Select一次性创建所有任务,再 WhenAll

四、运行示例

Please input text type as you want, you can input more:>>>a.txt, b.txt
Created successfully!!!! File Name:a.txt
Created successfully!!!! File Name:b.txt
所有文件创建完成,共 2 个。
是否需要为该文件:...\a.txt,写入内容?Y/N:>>y
请输入你要往...\a.txt 文件传输的内容:>>hello world hello C# is powerful
文件...\a.txt,成功被写入
是否需要为该文件:...\b.txt,写入内容?Y/N:>>n
用户不进行写入,该文件:...\b.txt
Please input the specify file:>a.txt
1 words
文件名:a.txt
Please input query word:>>hello
该文件 a.txt 中的搜索词 hello 查询出现的频率: 2 次
是否退出?(Y/N)::>>y
CreateTxt Method is Done

五、可以继续优化的方向

六、总结

通过本项目,我掌握了以下 C# 核心技术:

本项目可以作为学习上位机开发的起点,后续可以扩展为工业数据采集与监控系统

以上就是使用C#从零到完整实现的并发单词频率统计器的详细内容,更多关于C#并发单词频率统计器的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文