C#如何遍历数组并查找具有最新日期的数组?

时间:2018-08-31 20:24:09

标签: c# arrays linq sorting

此处是新的C#学习者。我已经浏览了许多已经发布在此处的问题;很抱歉,如果我错过了这样一个已经提出的问题。

背景

我使用的程序会生成Excel文件,这些文件的名称包含创建日期。产生了数千个需要排序的Excel文件。我的任务是从这些文件名中提取信息,以便在确认后将文件移至其适当的位置。我正在使用一个程序来成功找到具有特定字符串的所有关联文件。我已经将这些文件的名称存储在一个数组中。

示例文件名: IMPORTANT_NAME_LISTED(TEXT)[xx-xx-xx] [HH_MM] .xlsx

已知信息

日期以月/日/年的格式存储在“ []”中,并且100%一致(这意味着每个文件将产生相同的格式,日期大小和位置)。

我一直在尝试开发针对“。”的解决方案。在文件扩展名之前提取日期,但是我很挣扎。

我的策略

我有一个初步决定,请确保已存储所有文件名的数组包含值。

//code that extracts file names exists above
//file names which interest me are stored within "fileNameArray"

//Determine if the array that collected file names contains values

if (fileNameArray.Length > 1)
{
    for (int k = 0; k <= fileNameArray.Length; k++)
        {
            //Extract date from "[xx-xx-xx] [HH-MM]"
            //Transform MM/DD/YY to YY/MM/DD and temporarily store
            //Compare each date value that exist within the string
            //Target the most recent file - find the array index
            //(Ex: 20180831 - today's date)
        }
}

我的问题源于在保留数组索引的同时正确地解析了这些单独的数组项。

你们中的任何人都推荐一种使用方法吗? LINQ? Array.FindAll功能?

非常感谢您的帮助。

-克里斯

编辑: 有关我的情况的其他信息...

我有一个Excel文件目录,该目录可能超过1-3k个文件。我有一个程序,可以读取所有Excel文件的文件名。很多繁重的过滤/排序工作都发生在我要执行的代码之上。

我一直在努力解决同名文件的问题。例如:

我有4个文件,其中包含相同的部分名称"DILITHIUM_CRYSTYAL_FUEL_TIME"

我的程序必须能够通过核心名"DILITHIUM_CRYSTYAL_FUEL_TIME"来过滤/搜索文件名。如果我有多个同名文件,则需要能够以一种将文件名中的时间戳隔离出来并找到最新文件的方式来解析文件名。

我的文件将始终以100%一致的方式在文件扩展名的左侧显示时间戳。

我需要能够提取此时间戳,并与其他文件进行比较,并隔离最新的文件。

6 个答案:

答案 0 :(得分:2)

LINQ是一个不错的选择,结合Regex进行解析。

var dateRE = new Regex(@"\[(\d\d-\d\d-\d\d)\] \[(\d\d-\d\d)\](?=.xlsx)", RegexOptions.Compiled);

if (fileNameArray.Length > 0) {
    var ans = fileNameArray.Select((n, i) => {
                                var dtMatch = dateRE.Match(n);
                                return new { Filename = n, Index = i, Filedate = DateTime.ParseExact(dtMatch.Groups[1].Value+" "+dtMatch.Groups[2].Value, "MM-dd-yy HH-mm", CultureInfo.InvariantCulture) };
                            })
                           .OrderByDescending(nid => nid.Filedate)
                           .First();
}

如果要对文件名进行不同的处理,可以将First()替换为其他LINQ操作。

答案 1 :(得分:1)

我也会去进行正则表达式,字符串解析和linq:

此处的工作示例:https://dotnetfiddle.net/veUq2N

using System;
using System.Linq;
using System.Collections.Generic;
using System.Text.RegularExpressions;

public class Program
{
    private static Random random = new Random();
    private static Regex fileNameFragmentPattern = new Regex(@"\[(.*?)\]\.xlsx");
    private const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

    public static void Main()
    {
        var fileNames = new List<string>();

        // Generate random file names 
        for (var i = 0; i < 10000; i++) {
            fileNames.Add(RandomString(random.Next(8,10)) +  "_" + RandomString(random.Next(4,5)) + "_"  + "(TEXT) [" + RandomDate().ToString("MM-dd-yyyy") + "].xlsx");
        }

        // sort files by parsed dates
        var dateSortedFileNames = fileNames.OrderByDescending( f => ExtractDate(f));
        foreach (var fileName in dateSortedFileNames) {
            // you can do anything with sorted files here (or anywhere else below :)
            Console.WriteLine(fileName);
        }       
    }

    public static DateTime ExtractDate(string fileName) {
        var fragment = fileNameFragmentPattern.Match(fileName).Value;
        var month = int.Parse(fragment.Substring(1,2));
        var day = int.Parse(fragment.Substring(4,2));
        var year = int.Parse(fragment.Substring(7,4));
        return new DateTime(year, month, day);      
    }

    public static string RandomString(int length)
    {
        return new string(Enumerable.Repeat(chars, length)
          .Select(s => s[random.Next(s.Length)]).ToArray());
    }

    public static DateTime RandomDate(int min = -9999, int max = 9999)
    {
        return DateTime.Now.AddDays(random.Next(min,max));
    }
}

答案 2 :(得分:0)

这是一个非正则表达式的解决方案。

var files = new List<string>
{
    "IMPORTANT_NAME_LISTED (TEXT) [05-26-92].xlsx",
    "IMPORTANT_NAME_LISTED (TEXT) [11-02-89].xlsx",
    "IMPORTANT_NAME_LISTED (TEXT) [02-21-96].xlsx"
};

foreach (var fileName in files)
{
    var nameOnly = Path.GetFileNameWithoutExtension(fileName);
    var dateStr = nameOnly.Substring(nameOnly.Length - 9, 8);
    if (DateTime.TryParseExact(dateStr, "MM-dd-yy", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime date))
        Console.WriteLine(date.ToShortDateString());
}

由于您提到文件名的“日期”部分是100%一致的,并且我们知道“日期”的长度始终为8。因此,使用该知识,

nameOnly.Substring(nameOnly.Length - 9, 8);

将提取第一个[之后的字符串,并提取8之前的]个字符。

如果您100%肯定文件扩展名始终为.xlsx,则可以进一步缩短代码。

foreach (var fileName in files)
{
    var dateStr = fileName.Substring(fileName.Length - 14, 8);
    if (DateTime.TryParseExact(dateStr, "MM-dd-yy", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime date))
        Console.WriteLine(date.ToShortDateString());    
}

答案 3 :(得分:0)

我想重新发布一下我过去用来解决编程问题的文章。过去一周左右一直很忙,对于延迟,我深表歉意。

这是我的代码中的一段,很好地解决了我的问题:

string scanToolDateFinalStgT1 = "";
DateTime scanToolDateFinalT1 = new DateTime(2000, 1, 1, 1, 1, 00);

for (int k = 0; k < scanToolT1Pass.Count(); k++)
{
    string scanToolPassNameOnly = Path.GetFileNameWithoutExtension(scanToolT1Pass[k].ToString());
    string scanToolDateStr = scanToolPassNameOnly.Substring(scanToolPassNameOnly.IndexOf("[") + 1, 8);
    string scanToolTimeStr = scanToolPassNameOnly.Substring(scanToolPassNameOnly.LastIndexOf("[") + 1, 5);
    DateTime currentScanToolDate = DateTime.ParseExact(scanToolDateStr + " " + scanToolTimeStr, "MM-dd-yy HH_mm", null);
    if (currentScanToolDate > scanToolDateFinalT1)
    {
        scanToolDateFinalT1 = currentScanToolDate;
        scanToolDateFinalStgT1 = scanToolT1Pass[k].ToString();
    }
}

信息:

此代码段的目标是“ [xx-xx-xx] [xx-xx]。”,这是文件名的部分唯一标识符。

程序正在传入“ scanToolT1Pass”,它是文件名的数组。我的任务是获取此数组并解析文件名,找到最新的文件名。

“ DateTime scanToolDateFinalT1”的通用日期为1/01 / 200,1:01:00,严格将其用作基本比较点。我确信我的数据永远不需要2000年之前的日期。我试图让一个参考日期读取所有零以进行比较,但是VisualStudio不赞成。

说明:

是否存在更高级和/或适当的方法来解析此数据?我确定有。但是,对于初学者来说,这种方法对我来说很有意义,我的目标是在将来完善它。对我来说,最重要的是拥有一个首先运行的程序,而不是投入大量的研究来完善它。

我能够在整个程序中实现类似的for循环,从而以非常快的速度过滤大量数据。

再次感谢社区以及@Sach和@It Man,我能够将他们的反馈纳入我的解决方案中。

  • 克里斯

答案 4 :(得分:0)

更简单的选择:

var regex = new Regex(".*\[(.*)-(.*)] \[(.*)].*");

string latest = fileNameArray.OrderBy(s => regex.Replace(s, "$2$1$3")).Last();

可以在https://regex101.com/r/Ldh0sa

上看到模式的演示和说明。

答案 5 :(得分:-1)

public struct DatedExcelOutput
{
    public string FullName {  get; }
    public string Name { get; }
    public DateTime CreationDate { get; }

    public DatedExcelOutput(string fileName)
    {
        FullName = fileName;
        Name = getName();
        CreationDate = parseDate();
    }
}

可以这样称呼:

IEnumerable<string> fileNames = GetFiles();
var datedFiles = fileNames.Select(f => new DatedExcelOutput(f))
                                  .OrderBy(d => d.CreationDate);

您可能最终需要在UI中对这些升/降进行排序,对吗?因此,我认为丢弃日期信息没有任何意义。

编辑:删除NetMage指出的不必要的IO调用。