如何从没有包含数据的文本文件中读取?

How to read from a text file which does not have consisted data?

我有一个文本文件,其中存储了有关生产信息和过程的信息。该过程由几个阶段组成,例如 StrippingCleaningPaint 等。这些阶段存储的日期可能是 expected datestart datefinished date, delay date.

示例 .txt 文件:

567,Eindhoven,21,Stripping_e=20/05/2020,Stripping_s=21/05/2020,Stripping_f=22/05/2020,Cleaning_e=23/05/2020,Cleaning_s=27/05/2020,Cleaning_f=28/05/2020,Paint_e=28/05/2020,Panint_s=28/05/2020,Paint_f=29/05/2020,Cabinet_e=29/05/2020,Cabinet_s=31/05/2020,Cabinet_f=,Table_e=,Table_s=,Table_f=,Stand_e=,Stand_s=,Stand_f=,Display_e=,Display_s=,Display_f=,UControls_e=,UControls_s=,UControls_f=,Test_e=,Test_s=,Test_f=,Prepack_e=,Prepack_s=,Prepack_f=,Endpack_e=,Endpack_s=,Endpack_f=,Release_e=,Release_s=,Release_f=,

读完这个 .txt 文件后,我将它存储在 dataGridView 中,如下所示:

但是,我不知道如何将最新日期添加到相应的列中。如果我在文本文件中看到 Stripping_f,我想覆盖 Stripping 列中的 Stripping_e 值。其余列也一样。这也是我不一定有专栏的日期(预期、开始、完成或延迟)。

这是我目前使用的代码:

private void IGT_Load(object sender, EventArgs e)
        {
            table.Columns.Add("SalesNr", typeof(int));
            table.Columns.Add("PName", typeof(string));
            table.Columns.Add("Type", typeof(int));
            table.Columns.Add("Stripping", typeof(string));
            table.Columns.Add("Cleaning", typeof(string));
            table.Columns.Add("Paint", typeof(string));
            table.Columns.Add("Cabinet", typeof(string));
            table.Columns.Add("Table", typeof(string));
            table.Columns.Add("Stand", typeof(string));
            table.Columns.Add("Display", typeof(string));
            table.Columns.Add("User Controls", typeof(string));
            table.Columns.Add("Test", typeof(string));
            table.Columns.Add("Pre-pack", typeof(string));
            table.Columns.Add("End-pack", typeof(string));
            table.Columns.Add("Release for Delivery", typeof(string));
            dataGridView1.DataSource = table;
            Import();
        }

        private void Import()
        {
            // get lines from the text file
            string[] lines = File.ReadAllLines(@"..\..7.txt");
            string[] values;


            for (int i = 0; i < lines.Length; i++)
            {
                values = lines[i].ToString().Split(',', '=');
                string[] row = new string[values.Length];

                for (int j = 0; j < values.Length; j++)
                {
                    
                    if(values[j] == "Stripping_e")
                    {
                        row[j] = values[j + 1].Trim();
                        j++;
                    }
                    else if (values[j] == "Stripping_s")
                    {
                        row[j-2] = values[j+1].Trim();
                        j++;
                    }
                    else if(values[j] == "Cleaning_s" || values[j] == "Cleaning_e" || values[j] == "Cleaning_f" || values[j] == "Cleaning_d")
                    {
                        row[j] = values[j + 1].Trim();
                    }
                    else
                    {
                        row[j] = values[j].Trim();
                    }
                }
                table.Rows.Add(row);
            }
        }

table 是连接到 dataGridView1DataTable 作为 DataSource

我以前从未使用过文本文件,所以非常感谢您的帮助!

读取 semi-unstructured CSV 可能很棘手,您必须坚信 CSV 文件始终是正确的。例如,如果某个类别的其中一个日期拼写错误,例如……“Panint_s=28/05/2020”……在发布的数据中……那么,在下面的代码中,这种拼写错误不会引发任何类型的错误错误或消息,它永远不会被使用。所以,编码器要小心。下面是一种这样的“hacky”方法。

您可能遇到的一个问题是当前代码如何使用字符串数组 (string[])。这将使事情变得更加复杂。当代码到达 SomeCategory=SomeDate 点时,它很有用。下面,一个字符串数组用于这些拆分以获取类别的日期。在代码的其他地方,它使用 List<string> 来分隔数据中的行。当我们想要获取“特定”类别的日期时,这将有所帮助。更多内容在下方。

因此,给定一个 string 数据,如您显示的那样,string 是网格中的“单个”行,那么我会用逗号“拆分”该字符串”, ”。然后将字符串数组转换为List<string>。它可能看起来像……

List<string> SplitRow = curRow.Split(',').ToList();

鉴于已发布的单行数据,此 List<string> allData 可能类似于……

可以看出,我们将使用此 List<string> 来获取网格中前三个单元格的数据,并且当我们到达我们想要获取“最新”日期的代码部分时对于特定类别,我们可以“过滤”此 List<string> 以仅包含我们想要的类别。如果 allData 是上面的 List<string>,那么要过滤列表以仅包含我们想要的类别,代码可能类似于...

String targetCategory = “Stripping”;
List<string> categoryData = allData.Where(x => x.Contains(targetCategory)).ToList();

这将产生一个 List<string> categoryData 只有“剥离”类别。像下面...

使用此列表,我们可以遍历所有字符串,解析日期和 return 所有给定日期的最新日期。如果我们创建一个方法来 returns 这个日期字符串,那么我们可以使用它来为每一行分配网格中的每个类别列。

因此,采用 allData 列表和 string 类别进行过滤的方法……可能会派上用场。此方法可能如下所示……

private string GetLatestDateForCategory(List<string> allData, string targetCategory) {
  List<string> categoryData = allData.Where(x => x.Contains(targetCategory)).ToList();
  DateTime latestDate = DateTime.MinValue;
  string[] splitArray;
  foreach (string catLine in categoryData) {
    splitArray = catLine.Split('=');
    if (splitArray.Length >= 2) {
      if (DateTime.TryParseExact(splitArray[1], "dd/MM/yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime date)) {
        if (date > latestDate) {
          latestDate = date;
        }
      } 
    }
  }
  if (latestDate != DateTime.MinValue) {
    return latestDate.ToShortDateString();
  }
  return "";
}

此方法的演练将从“过滤”所有数据开始,以获得 targetCategory 字符串值的列表。然后创建一个 DateTime 变量来比较日期并用作最新日期。我们将日期设置为“最小”值,我们将使用此“最小”日期来指示该类别在数据中没有日期。接下来是字符串数组splitArray,用于在“=”字符上拆分每个字符串。

开始循环遍历列表中所有已过滤的字符串。在“=”字符上拆分字符串后,将进行检查以确保字符串中有日期。如果有日期字符串,那么该日期将被解析为 DateTime 对象以便于比较。如果日期解析成功,则代码将检查解析的日期是否为最新日期。如果解析的日期晚于当前 latestDate,则会更新以反映这个更新的最新日期。

检查完所有日期后,代码将检查 latestDate。如果 latestDate 仍然是“最小”值,那么这意味着没有找到该类别的日期并且空字符串是 returned,否则代码将 return 最新日期字符串.

如果代码循环遍历有很多行的数据中的每个字符串,那么当我们想要从特定类别中获取最新日期时,上述方法会派上用场。

因此,将其放在一起可能如下所示……

private void Import() {
  List<string> allLines = File.ReadAllLines(@"D:\Test\CSV7.txt").ToList();
  DataRow dr;
  List<string> SplitRow;
  int value;
  foreach (string curRow in allLines) {
    dr = table.NewRow();
    SplitRow = curRow.Split(',').ToList();
    if (SplitRow.Count >= 3) {
      value = 0;
      if (int.TryParse(SplitRow[0], out value)) {
        dr["SalesNr"] = value;
      }
      if (int.TryParse(SplitRow[2], out value)) {
        dr["Type"] = value;
      }
      dr["PName"] = SplitRow[1];
      dr["Stripping"] = GetLatestDateForCategory(SplitRow, "Stripping");
      dr["Cleaning"] = GetLatestDateForCategory(SplitRow, "Cleaning");
      dr["Paint"] = GetLatestDateForCategory(SplitRow, "Paint");
      dr["Cabinet"] = GetLatestDateForCategory(SplitRow, "Cabinet");
      dr["Table"] = GetLatestDateForCategory(SplitRow, "Table");
      dr["Stand"] = GetLatestDateForCategory(SplitRow, "Stand");
      dr["Display"] = GetLatestDateForCategory(SplitRow, "Display");
      dr["User Controls"] = GetLatestDateForCategory(SplitRow, "UControls");
      table.Rows.Add(dr);
    }
  }
}

要了解此方法,首先要从 CSV 文件中获取 List<string>。在发布的数据中,这将是列表中的单个字符串。接下来定义了一个 DataRow dr ,我们将使用这一行来添加我们从数据中获得的值。另一个 List<string> SplitRow 用于列表中的“每个”字符串。最后,一个 int value 用于解析 intSalesNrType.

开始循环遍历所有数据中的每个字符串,从现有的 table 创建一个新的 DataRow,然后用逗号“;”拆分当前字符串。进行检查以确保 SalesNr、PName 和 Type 至少有三 (3) 个值。解析 SalesNrType 值,然后将 PName 字符串值设置到新行中。然后,每个“类别”将使用上述方法为每个类别设置日期字符串。最后,该行被添加到 table。注:代码中并非所有类别都填写

我希望这是有道理的。它很笨拙,但在我的测试中有效。祝你好运。

这就是我的做法。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Globalization;

namespace WindowsFormsApplication71
{
    public partial class Form1 : Form
    {
        const string FILENAME = @"c:\temp\test.csv";
        public Form1()
        {
            InitializeComponent();

            StreamReader reader = new StreamReader(FILENAME);
            string line = "";
            int row = 0;
            DataTable dt = new DataTable();
            while ((line = reader.ReadLine()) != null)
            {
                line = line.Trim();
                if (line.Length > 0)
                {
                    string[] columns = line.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToArray();
                    List<object> newRow = new List<object>(); ;
                    if (++row == 1)
                    {
                        dt.Columns.Add("SalesNr", typeof(string));
                        dt.Columns.Add("PName", typeof(string));
                        dt.Columns.Add("Type", typeof(int));
                    }
                    for (int col = 0; col < columns.Length; col++)
                    {
                        switch (col)
                        {
                            case 0:
                                newRow.Add(columns[0]);
                                break;
                            case 1:
                                newRow.Add(columns[1]);
                                break;
                            case 2:
                                newRow.Add(columns[2]);
                                break;
                            default:
                                string[] keyValue = columns[col].Split(new char[] { '=' });
                                if (row == 1)
                                {
                                    dt.Columns.Add(keyValue[0], typeof(DateTime));
                                }
                                if (keyValue[1].Trim().Length > 0)
                                {
                                    newRow.Add(DateTime.ParseExact(keyValue[1], "dd/MM/yyyy", CultureInfo.InvariantCulture));
                                }
                                else
                                {
                                    newRow.Add(null);
                                }
                                break;
                        }
                    }
                    dt.Rows.Add(newRow.ToArray());
                }
            }
            dataGridView1.DataSource = dt;
        }
    }
}