重构C# CSV解析器与数据聚合:字典排序及区域解析最佳实践
问题解答:优化C#电影数据CSV读取程序
一、解决区域适配问题:正确解析小数
直接替换小数点的方式依赖系统区域设置,风险极高。CSV文件的小数分隔符通常遵循不变文化(InvariantCulture)(即小数点为.),因此解析时应显式指定文化信息,无需手动替换字符:
- 添加命名空间:
using System.Globalization; - 修改
Film构造函数中的小数解析代码:
// 替换原有的Replace方式 AtlagosJegyar = double.Parse(sor2[6], CultureInfo.InvariantCulture);
如果CSV文件的小数分隔符确实是逗号(如本地化CSV),可指定对应文化(如CultureInfo.GetCultureInfo("hu-HU")),但显式指定文化始终比字符替换更稳健。
二、用LINQ与现代.NET特性优化代码
1. 简化文件读取逻辑
原Beolvasas方法的循环可通过LINQ+File.ReadLines(延迟加载,内存效率更高)替换,一行完成数据读取、过滤和转换:
public static List<Film> Beolvasas(string fajlnev) { return File.ReadLines(fajlnev) .Skip(1) // 跳过表头行 .Where(line => !string.IsNullOrWhiteSpace(line)) .Select(line => new Film(line)) .ToList(); }
File.ReadLines相比ReadAllLines,不会一次性加载所有行到内存,适合处理大文件。
2. 简化Feladat6:计算平均观众数
原循环累加可通过LINQ的Average直接实现,代码更简洁:
static void Feladat6() { double atlag = filmek.Average(f => f.Nezoszam); Console.WriteLine("6.feladat: A filmek átlagos nézőszáma {0:0.0} fő", atlag); }
3. 用LINQ GroupBy彻底简化Feladat8:按类型统计观众数
完全无需手动填充Dictionary,GroupBy可直接按电影类型分组,对每组观众数求和后排序:
static void Feladat8() { Console.WriteLine("8. feladat: Műfajok összesített nézőszáma:"); var mufajStatisztika = filmek .GroupBy(f => f.Mufaj) .Select(g => new { Mufaj = g.Key, OsszNezoszam = g.Sum(f => f.Nezoszam) }) .OrderByDescending(item => item.OsszNezoszam); foreach (var item in mufajStatisztika) { Console.WriteLine($"\t- {item.Mufaj}: {item.OsszNezoszam} fő"); } }
这段代码逻辑清晰,避免了手动处理字典键的存在性判断,符合地道的C# LINQ风格。
三、完整优化后代码
using System; using System.Collections.Generic; using System.Linq; using System.IO; using System.Globalization; namespace filmapp { internal class Film { public int Sorszam { get; set; } public string Cim { get; set; } public string Mufaj { get; set; } public int BemutatasiEv { get; set; } public string Rendezo { get; set; } public int Nezoszam { get; set; } public double AtlagosJegyar { get; set; } public Film(string sor) { string[] sor2 = sor.Split(';'); Sorszam = int.Parse(sor2[0]); Cim = sor2[1]; Mufaj = sor2[2]; BemutatasiEv = int.Parse(sor2[3]); Rendezo = sor2[4]; Nezoszam = int.Parse(sor2[5]); // 显式使用不变文化解析小数,避免区域问题 AtlagosJegyar = double.Parse(sor2[6], CultureInfo.InvariantCulture); } public static List<Film> Beolvasas(string fajlnev) { return File.ReadLines(fajlnev) .Skip(1) .Where(line => !string.IsNullOrWhiteSpace(line)) .Select(line => new Film(line)) .ToList(); } } internal class Program { static List<Film> filmek = new List<Film>(); static void Main(string[] args) { Feladat4(); Feladat5(); Feladat6(); Feladat7(); Feladat8(); Console.ReadKey(); } static void Feladat4() => filmek = Film.Beolvasas("filmek.csv"); static void Feladat5() => Console.WriteLine("5. feladat: {0} film található az állományban", filmek.Count); static void Feladat6() { double atlag = filmek.Average(f => f.Nezoszam); Console.WriteLine("6.feladat: A filmek átlagos nézőszáma {0:0.0} fő", atlag); } static void Feladat7() { Console.WriteLine("7. feladat: 2018 után megjelent, népszerű filmek:"); foreach (var f in filmek.Where(x => x.Nezoszam > 1000000 && x.BemutatasiEv > 2018)) { Console.WriteLine($"\t-{f.Cim} ({f.BemutatasiEv})"); } } static void Feladat8() { Console.WriteLine("8. feladat: Műfajok összesített nézőszáma:"); var mufajStatisztika = filmek .GroupBy(f => f.Mufaj) .Select(g => new { Mufaj = g.Key, OsszNezoszam = g.Sum(f => f.Nezoszam) }) .OrderByDescending(item => item.OsszNezoszam); foreach (var item in mufajStatisztika) { Console.WriteLine($"\t- {item.Mufaj}: {item.OsszNezoszam} fő"); } } } }
关键优化点总结
- 区域适配:用
CultureInfo.InvariantCulture显式解析小数,替代脆弱的字符替换 - 文件IO:使用
File.ReadLines实现延迟加载,提升大文件处理性能 - LINQ简化:用
GroupBy+Sum替代手动字典填充,用Average替代循环累加,代码更简洁易读 - 类型转换:用
int.Parse替代Convert.ToInt32,语义更明确(已知输入为有效整数时)
内容的提问来源于stack exchange,提问作者user32739011
相关产品推荐
相关产品推荐

