首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C#:读取文本文件并处理它

C#:读取文本文件并处理它
EN

Stack Overflow用户
提问于 2017-10-26 08:46:22
回答 2查看 117关注 0票数 0

我需要一个用C#写出来的程序

  1. 收音机里播放了多少首埃里克·克莱普顿的歌。
  2. 有没有所有三个电台播放的埃里克·克莱普顿歌曲。
  3. 他们播出埃里克·克莱普顿歌曲的时间总计有多少。

第一列包含无线电标识(1-2-3),第二列是关于歌曲播放时间分钟,第三列是以秒为单位的歌曲播放时间,最后两列是表演者:歌。

该文件如下所示:

1 5 3深紫色:坏态度

2336 Eric Clapton:Terraplane

3 2 46 Eric Clapton:疯狂的乡村霍普

3. 25 Omega:Ablakok

艾瑞克?克莱普顿:如果可以的话,抓住我。

埃里克·克莱普顿:威利与吉夫之手

3 4 33 Omega:A szamuzott

.还有670多条线。

到目前为止我了解到:

代码语言:javascript
复制
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.IO;

    namespace radiplaytime
    {
        public struct Adat
        {
            public int rad;
            public int min;
            public int sec;
            public Adat(string a, string b, string c)
            {
                rad = Convert.ToInt32(a);
                min = Convert.ToInt32(b);
                sec = Convert.ToInt32(c);
            }
        }
    class Program
    {
        static void Main(string[] args)
        {

            String[] lines = File.ReadAllLines(@"...\zenek.txt");
            List<Adat> adatlista = (from adat in lines
                                        //var adatlista = from adat in lines
                                    select new Adat(adat.Split(' ')[0],
                                                    adat.Split(' ')[1],
                                                    adat.Split(' ')[2])).ToList<Adat>();

            var timesum = (from adat in adatlista
                              group adat by adat.rad into ertekek
                              select new
                              {
                                  rad = ertekek.Key,
                                  hour = (ertekek.Sum(adat => adat.min) +
                                  ertekek.Sum(adat => adat.sec) / 60) / 60,

                                  min = (ertekek.Sum(adat => adat.min) +
                                  ertekek.Sum(adat => adat.sec) / 60) % 60,

                                  sec = ertekek.Sum(adat => adat.sec) % 60,

                              }).ToArray();
            for (int i = 0; i < timesum.Length; i++)
            { 
                Console.WriteLine("{0}. radio: {1}:{2}:{3} playtime",
                    timesum[i].rad,
                    timesum[i].hour,
                    timesum[i].min,
                    timesum[i].sec);
            }
            Console.ReadKey();
        }
    }
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-10-26 09:42:20

您可以定义一个自定义类来存储每一行的值。您将需要使用Regex来拆分每一行并填充自定义类。然后您可以使用linq来获取所需的信息。

代码语言:javascript
复制
public class Plays
    {
        public int RadioID { get; set; }
        public int PlayTimeMinutes { get; set; }
        public int PlayTimeSeconds { get; set; }
        public string Performer { get; set; }
        public string Song { get; set; }
    }

然后读取文件并填充自定义播放:

代码语言:javascript
复制
String[] lines = File.ReadAllLines(@"songs.txt");
List<Plays> plays = new List<Plays>();
foreach (string line in lines)
{
    var matches = Regex.Match(line, @"^(\d+)\s(\d+)\s(\d+)\s(.+)\:(.+)$"); //this will split your line into groups
    if (matches.Success)
    {
        Plays play = new Plays();
        play.RadioID = int.Parse(matches.Groups[1].Value);
        play.PlayTimeMinutes = int.Parse(matches.Groups[2].Value);
        play.PlayTimeSeconds = int.Parse(matches.Groups[3].Value);
        play.Performer = matches.Groups[4].Value;
        play.Song = matches.Groups[5].Value;
        plays.Add(play);
    }
}

现在已经有了歌曲列表,您可以使用linq获得所需的内容:

代码语言:javascript
复制
 //Get Total Eric Clapton songs played - assuming distinct songs
 var ericClaptonSongsPlayed = plays.Where(x => x.Performer == "Eric Clapton").GroupBy(y => y.Song).Count();

//get eric clapton songs played on all radio stations
var radioStations = plays.Select(x => x.RadioID).Distinct();
var commonEricClaptonSong = plays.Where(x => x.Performer == "Eric Clapton").GroupBy(y => y.Song).Where(z => z.Count() == radioStations.Count());

等。

票数 1
EN

Stack Overflow用户

发布于 2017-10-26 09:53:32

只有当文本非常简单且不需要处理固定长度字段时,字符串拆分才能工作。它还会生成许多临时字符串,这会导致程序消耗内存中原始字符串大小的很多倍,并由于不断的分配和垃圾收集而损害性能。

Riv的答案显示了如何使用Regex解析这个文件。不过,它可以通过几种方式加以改进:

代码语言:javascript
复制
var pattern=@"^(\d+)\s(\d+)\s(\d+)\s(.+)\:(.+)$";
var regex=new Regex(pattern);
var plays = from line in File.ReadLines(filePath)
            let matches=regex.Match(line)
            select new Plays {
                          RadioID = int.Parse(matches.Groups[1].Value),
                          PlayTimeMinutes = int.Parse(matches.Groups[2].Value),
                          PlayTimeSeconds = int.Parse(matches.Groups[3].Value),
                          Performer = matches.Groups[4].Value,
                          Song = matches.Groups[5].Value 
                       };
  1. ReadLines返回一个IEnumerable<string>,而不是返回缓冲区中的所有行。这意味着解析可以立即开始。
  2. 通过使用单个正则表达式,我们不必为每一行重新构建正则表达式。
  3. 不需要任何名单。查询返回可以直接应用其他LINQ操作的IEnumerable。

例如:

代码语言:javascript
复制
var durations = plays.GroupBy(p=>p.RadioID)
                     .Select(grp=>new { RadioID=grp.Key,
                                        Hours = grp.Sum(p=>p.PlayTimeMinutes + p.PlayTimeSecons/60)/60,)
                                        Mins = grp.Sum(p=>p.PlayTimeMinutes + p.PlayTimeSecons/60)%60,)
                                        Secss = grp.Sum(p=> p.PlayTimeSecons)%60)
                              });

一个更大的改进可以是给这些团体取名字:

代码语言:javascript
复制
var pattern=@"^(?<station>\d+)\s(?<min>\d+)\s(?<sec>\d+)\s(?<performer>.+)\:(?<song>.+)$";

...
            select new Plays {
                          RadioID = int.Parse(matches.Groups["station"].Value),
                          PlayTimeMinutes = int.Parse(matches.Groups["min"].Value),
...
                       };

您还可以去掉Plays类并使用一个稍微复杂一些的LINQ查询:

代码语言:javascript
复制
var durations = from line in File.ReadLines(filePath)
            let matches=regex.Match(line)
            let play= new {
                          RadioID = int.Parse(matches.Groups["station"].Value),
                          Minutes = int.Parse(matches.Groups["min"].Value),
                          Seconds = int.Parse(matches.Groups["sec"].Value)
                       }
            group play by play.RadioID into grp
            select new { RadioID = grp.Key,
                         Hours   = grp.Sum(p=>p.Minutes + p.Seconds/60)/60,)
                         Mins    = grp.Sum(p=>p.Minutes + p.Seconds/60)%60,)
                         Secs    = grp.Sum(p=> p.Seconds)%60)
            };

在这种情况下,不为PerformerSong生成字符串。这是正则表达式的另一个好处。匹配和组只是原始字符串的索引。在读取.Value之前不生成字符串。这将使这种情况下使用的RAM减少大约75%。

一旦得到了结果,就可以迭代它们:

代码语言:javascript
复制
foreach (var duration in durations)
{ 
    Console.WriteLine("{0}. radio: {1}:{2}:{3} playtime",
        duration.RadioID,
        duration.Hours,
        duration.Mins,
        duration.Secs);
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46949668

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档