【问题标题】:C# How to loop through an array and find array with most recent date?C#如何遍历数组并找到最近日期的数组?
【发布时间】:2018-08-31 20:24:08
【问题描述】:

这里是新的 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)
        }
}

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

你们有没有推荐使用的方法? 林克? Array.FindAll 功能?

非常感谢您的帮助。

-克里斯

编辑: 关于我的情况的更多信息......

我有一个 Excel 文件目录,其中可能超过 ~1-3k 个文件。我有一个程序可以读取所有 Excel 文件的文件名。很多繁重的过滤/排序发生在我想要实现的上面的代码之前。

我一直在努力解决与处理同名文件有关的问题。例如:

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

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

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

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

【问题讨论】:

  • 文件名没有日期属性。您可能希望您的数组为 FileInfo
  • 我认为不要使用数组使用 List
  • 您可以使用正则表达式从文件名中提取日期。
  • 正在使用的程序生成的文件名始终包含文件日期信息,包含在这种格式 [MM-DD-YY] 的括号内。
  • 如果您的项目中有另一段代码对日期有用,则可以创建一个struct,它在构造函数中接收文件名并解析日期。使用起来更容易。

标签: c# arrays linq sorting


【解决方案1】:

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 操作。

【讨论】:

  • NetMage,感谢您的意见。我需要对 Regex 感到更加自在,因为它非常强大……尽管做出这些陈述让我感到沮丧。编辑:我肯定会使用其中的一部分。
  • @ChrisS 虽然细节可能不同,但Regex101 等网站可以帮助创建 RE(使用 PCRE 模式)。
  • 我更新了模式和代码来处理时间。
  • 优秀。我在您提到的网站上验证了 RE。这非常有帮助。谢谢!
【解决方案2】:

我也会选择正则表达式、字符串解析和 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));
    }
}

【讨论】:

  • 这不会创建数千次新的正则表达式吗?
  • IT 人,谢谢!我将使用其中的一部分来帮助我开发适用于我的程序的解决方案。
  • @ParrishHusband 已修复。那里没有性能要求,但无论如何最好消除这种缺点。
  • @ITMan 是的,我同意。我在我的身上增加了额外的开销,也需要修复。多对代码的关注是很好的。
【解决方案3】:

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

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());    
}

【讨论】:

  • 萨赫,感谢您的意见。我非常喜欢这个例子。我将在今晚/周末开发解决方案,并一定会报告!
  • 不客气。为了安全起见,您应该使用DateTime.TryParseExact() 而不是DateTime.ParseExact()。你永远不能太小心。我已经编辑了示例。
【解决方案4】:

我想再次在这里发布我用来解决编程问题的内容。过去一周左右一直很忙,我为延迟道歉。

这是我的代码中的一个 sn-p,它完美地解决了我的问题:

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();
    }
}

信息:

此 sn-p 旨在针对“[xx-xx-xx] [xx-xx].”,它是文件名的部分唯一标识符。

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

'DateTime scanToolDateFinalT1' 的通用日期为 200 年 1 月 1 日 1:01:00,严格用作基准比较点。我确信我的数据永远不会需要 2000 年之前发生的日期。我试图让参考日期读取全零来进行比较,但 VisualStudio 不同意。

说明:

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

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

再次感谢社区和 @Sach 和 @It Man,我能够将他们的回复融入我的解决方案中。

  • 克里斯

【讨论】:

    【解决方案5】:

    更简单的选择:

    var regex = new Regex(".*\[(.*)-(.*)] \[(.*)].*");
    
    string latest = fileNameArray.OrderBy(s => regex.Replace(s, "$2$1$3")).Last();
    

    模式的演示和解释可以看https://regex101.com/r/Ldh0sa

    【讨论】:

      【解决方案6】:
      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 调用。

      【讨论】:

      • 为什么使用结构而不是类? parseDate 在哪里实现?
      • @Amy,因为它很小,而且预期的行为是不变的。当然可以讨论。如果用途发生变化,重新评估struct 是否有意义总是公平的。
      • 由于 OP 声明文件名是通过查找与字符串匹配的文件名来检索的,因此无需再次测试文件是否存在等。
      • 不喜欢无缘无故进行涉及文件系统的操作。您的运行速度会比必要的慢得多,并且考虑到涉及数以千计的文件,您的性能下降可能会很明显。
      • 帕里什,感谢您的意见!我现在有很多事情需要考虑。我非常感谢您的帮助。
      猜你喜欢
      • 1970-01-01
      • 2017-12-16
      • 2019-07-09
      • 1970-01-01
      • 2022-10-01
      • 2017-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多