以下代码......
var lightningFileNames = ConfigurationManager.AppSettings["LightningFileNames"];
var files = Directory.GetFiles(mapPath, lightningFileNames);
List<List<LightningStrikeModel>> ltgStrikes = new List<List<LightningStrikeModel>>();
foreach (string file in files)
{
var stringData = new List<string>();
using (var reader = new StreamReader(file))
{
while (!reader.EndOfStream)
{
var data = reader.ReadLine().Trim();
if (!string.IsNullOrEmpty(data))
{
stringData.Add(data);
}
}
reader.Close();
}
//extracted from file name to get an orderby
int lgtTemp = int.Parse(Regex.Match(file, @"\d+").Value);
ltgStrikes.Add((from item in stringData
select item.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
into rawData
where rawData.Length >= 4
select new LightningStrikeModel
{
Date = rawData[0],
Time = rawData[1],
Latitude = Convert.ToDecimal(rawData[2]),
Longitude = Convert.ToDecimal(rawData[3]),
Polarity = Convert.ToDecimal(rawData[4]),
orderBy = lgtTemp
}).ToList());
}
var tempLtg = ltgStrikes
.SelectMany(record => record)
.OrderBy(record => record.orderBy)
.GroupBy(record => record.orderBy).ToList();
return ltgStrikes;
文件名为ltg_1.txt,ltg_2.txt ... ltg_12.txt
我的问题因三件事而存在。
1)因为我要去一个文件夹位置来获取文件列表以读取数据并填充列表,我按照它们在文件夹中的顺序得到它们 - 所以我会按顺序读取文件
_1.txt,_10.txt,_11.txt,_12.txt,_2.txt等
我无法更改文件名。
2)有些文件中没有任何内容 - 空白文件。但是我仍然需要阅读它&#39;并在我的列表中添加占位符&gt; ltgStrikes - 我基本上需要有一个包含12个列表的列表,无论数据如何。
3)目前,无论数据如何,我都可以获得12个列表的列表,但它们的顺序错误,因为它们按照读取的顺序将它们添加到ltgStrikes中。所以
_1.txt的索引为[0],_10.txt的索引为[1],但在最终结果中,它的索引应为[9],_5。txt的索引为[8]但应该是[4]
我尝试了类似下面的内容,但由于某些文件为空,我没有得到12个列表的列表。我当前的数据只给出了2个列表的列表,因为其中只有2个数据文件。
var tempLtg = ltgStrikes
.SelectMany(record => record)
.OrderBy(record => record.orderBy)
.GroupBy(record => record.orderBy).ToList();
我没看到什么?仅供参考 - orderBy不用于在此处订购数据,但最终可以。我需要在应用程序的另一部分
答案 0 :(得分:7)
你在这里遇到了一大堆问题,因为你正在以错误的顺序做事。如果你像这样用LINQ混合循环,那么如果你把整个事情变成一个没有循环的大查询,整个事情会好得多。让我们这样做:
return Directory.GetFiles(mapPath, lightningFileNames)
.InNaturalOrder() // You write this!
好的,现在我们按正确的顺序排列了一系列文件。接下来我们想要什么?修剪过的文件内容。
.Select(f => File.ReadLines(f)
.Select(l => l.Trim())
.Where(l => l != ""))
好的,现在我们有一系列字符串序列。我们想要什么? LightningStrikeModels列表。所以我们将每个字符串转换为模型。这给了我们一系列模型。
.Select (stringData =>
(from item in stringData
select item.Split(new[] { ' ' },
StringSplitOptions.RemoveEmptyEntries)
into rawData
where rawData.Length >= 5
select new LightningStrikeModel (...))
我们将每个模型序列转换为模型列表。
.ToList())
我们现在有一系列模型列表。我们想要一个模型列表列表:
.ToList();
我们已经完成了。我们有一个模型列表列表,我们可以将其返回。
但是,不要止步于此。 当您完成编写代码时,请问自己是否可以做得更好。
如果我们这样做,那么我们立即看到Trim和空字符串的过滤器是完全没必要的。为什么?因为我们要接受该字符串,将其拆分为空格,消除空子串,并丢弃空格之间少于四个子串的任何字符串。那么为什么我们还要修剪前导和尾随空格,并消除空条目?拆分将执行前者,并检查后者是否有四个子串。所以我们可以更简单:
return Directory.GetFiles(mapPath, lightningFileNames)
.InNaturalOrder()
.Select(f => File.ReadLines(f))
.Select (stringData =>
...
现在再做一遍。我们可以让这个更简单吗?是。我们连续两个选择,所以我们可以合并它们。
return Directory.GetFiles(mapPath, lightningFileNames)
.InNaturalOrder()
.Select (f =>
(from item in File.ReadLines(f)
select item.Split(new[] { ' ' },
StringSplitOptions.RemoveEmptyEntries)
into rawData
where rawData.Length >= 5
select new LightningStrikeModel (...))
我们可以做得更好吗?哦,我们可以。我们可以做两件事:分配器助手和工厂。
static string[] SpaceSplit(this string s) => s.Split( ... );
static LightningStrikeModel BuildModel(this string[] parts) => new ...
现在我们的查询是
return Directory.GetFiles(mapPath, lightningFileNames)
.InNaturalOrder()
.Select (f =>
File.ReadLines(f)
.Select(line => line.SpaceSplit())
.Where(rawData => rawData.Length >= 5)
.Select(rawData => rawData.BuildModel())
.ToList())
.ToList();
OMG看看这个解决方案与我们开始时的混乱相比有多么简洁和清洁。看看它是多么清楚正确。并且易于理解和维护。 总是问自己是否可以做得更好。
我们可以更好地解决这个问题吗?我们可以!例如,我们可以注意到我们没有对字符串是否干净地转换为小数进行错误检查。如果他们不干嘛怎么办?该错误应该以某种方式处理,但现在它不是。想想你如何以一种不会使呼叫站点更难理解的方式来解决这个问题。
答案 1 :(得分:0)
如果您想按顺序阅读文件,可以在foreach
中订购files
foreach(string file in files.OrderBy(x=>int.Parse(Regex.Match(x,@"(\d+)\.txt").Groups[1].Value))){