Streamreader使用打开的"文件"对话框添加列



在下面的代码中,我希望在一个方法中打开我的OpenFileDialog,直到选择一个有效的文件。这只能在有条件的情况下起作用。出于某种原因,它在显示消息后添加了一列。如果我以前选择了一个不正确的文件,这会导致正确的数据表也被错误地读取。

public static InputData GetCSVData()
{
InputData InputData = new InputData();
OpenFileDialog OFDReader = new OpenFileDialog();
//Filter OpenFileDialog; show only CSV-Files
OFDReader.Filter = "CSV files|*.csv;";
// check if data contains "Date/Time" .
OFDReader.FileOk += delegate (object s, CancelEventArgs ev)
{                                 
//search for Line to start reader
int LineCounter = 0;
var readertmp = new StreamReader(OFDReader.FileName);
while (true)
{
string LineTmp = readertmp.ReadLine();
string record = "Date/Time";
if (LineTmp.Contains(record))
{ break; }
else if (readertmp.EndOfStream)
{
MessageBox.Show("Data has no DataPoints !", "Wrong Data", MessageBoxButtons.OK, MessageBoxIcon.Warning);
ev.Cancel = true; 
{ break; }
}
LineCounter++;
}
//read InputData
var reader = new StreamReader(OFDReader.FileName);
for (int i = 0; i < LineCounter; i++)
{
reader.ReadLine();
}
// settings CSVHelper
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
Delimiter = ";", // Set delimiter
};
var csv = new CsvReader(reader, config);
var DataRead = new CsvDataReader(csv);
InputData.DataTable.Load(DataRead);
//check for enough columns
int ColumnCounter = 0;
ColumnCounter = InputData.DataTable.Columns.Count;
if (ColumnCounter <= 2)
{
MessageBox.Show("Data has not enough columns!", "Wrong Data", MessageBoxButtons.OK, MessageBoxIcon.Warning);
ev.Cancel = true;
}
};
if (OFDReader.ShowDialog() == DialogResult.OK)
{
InputData.FilePath = OFDReader.FileName;
}
return InputData;
}
}

你似乎让这件事变得比必须的更复杂。对于初学者来说,你会为FileOK委托而烦恼似乎很奇怪(至少对我来说)。我看不出如果向用户呈现一次、两次或多次OpenFileDialog会有什么不同。使用单个OpenFileDialog似乎是在浪费精力。

如果用户选择了一个文件,但它不能满足必要的要求,那么只需打开另一个OpenFileDialog并让用户重试。在一个对话框中这样做当然是可能的,但是,"在哪里"你会使用它?这个对话框似乎是"特定"于"特定"类型的文件,为什么要将对话框限制为我们需要的要求。我认为一个简单的方法会一直循环,直到用户选择一个有效的文件或取消OpenFileDialog,这将是一个更容易的方法。

话虽如此,遵循您的代码有点奇怪。问题的原因是,无论文件是否具有数据点或足够的列,代码都会将文件读取到InputData.DataTable中。在线路上设置断点…

InputData.DataTable.Load(DataRead);

即使数据没有"DataPoints",你也会看到DataTable中充满了数据。在上面的代码行执行完接下来的几行之后,检查DataTable是否有2列或更多列数据。如果没有足够的列,那么代码只会弹出一个消息框来指示这一点。

这看起来很直接,但是InputData.DataTable仍然有数据,即使它很糟糕。下次调用上面的Load方法时,它将简单地将新表添加到现有表中。如果需要,它将添加列,并简单地将行添加到现有DataTable的底部。尝试打开几个BAD文件,然后最终打开好的文件,您会看到许多添加的列和行。

我想你可能会有这样的印象,当你打电话给…时

ev.Cancel = true;

代码就停在那里,然后回到委托中的第一行…

int LineCounter = 0;

……这不是真的。代码在执行ev.Cancel = true;之后继续。

这可以从以下事实中看出:每次试图打开BAD文件时,都会得到额外的列和行。一个简单的解决方案是在调用load方法之前创建一个"新"InputData对象。类似…

InputData = new InputData();
InputData.DataTable.Load(DataRead);

这将解决额外列的问题,但是,如果用户选择了一个BAD文件,并弹出错误消息,并且用户单击"确定"按钮返回到打开的文件对话框…然后…如果用户单击打开的文件对话"取消"按钮,BAD文件仍将显示在网格中。我相信你可能不想要这种行为。

没有详细介绍发布代码的其他一些奇怪方面。我提供了另一个可能的更简单的解决方案,如开头所述。当然,下面的代码使用了多个OpenFileDialog,但是用户在选择有效文件或取消对话框之前仍然无法转义。

下面的大部分代码都取自现有的发布代码,但是,它的结构不同。最初,在我们统计一个无休止的循环之前,会创建一些变量。具体来说,CsvConfiguration变量config添加了一些属性集,这些属性集在读取文件时忽略了一些代码崩溃问题。我相信您会希望设置CsvReader以按照您希望的方式处理这些问题。

一旦进入无休止的while循环,代码就会创建一个新的InputData对象,初始化一个新OpenFileDialog并设置其属性。然后,代码显示OpenFileDialog,当对话框返回时,DialogResultresult变量设置为对话框返回的DialogResult

如果对话框返回OK,则代码将检查该文件是否为"空"文件。如果文件是空的,则会显示一个消息框来通知用户,然后我们分支回到循环的乞求。如果对话框结果为Cancel,则代码将返回一个"新"InputData对象。空检查的原因是将在线路上抛出异常(未找到标头记录)…

DataRead = new CsvDataReader(csv);

如果文件为空。

我相信,可能有一些CsvHelper属性我错过了,可以防止这个"空"文件异常。如果有更好的方法来检查这个"空"文件或防止异常,我愿意接受建议。

如果该文件不是空的,我们将打开该文件并继续使用CsvDataReader读取其数据。其思想是……如果文件读取正确且没有错误并且符合要求,那么我们就已经设置了InputData.DataTable,剩下的就是设置其FilePath属性并返回InputData对象。

一旦我们有了InputData.DataTable,我们就可以检查InputData.DataTable中的列数。如果列数小于两(2),则向用户弹出错误消息框,并循环回while循环的乞求。

如果InputData.DataTable满足两(2)列或更多列的要求,则通过循环数据表中的所有列来进行另一次检查。如果至少有一(1)个列名是"Date/Time",那么我们就完成了对需求的检查,只需设置InputData.FileName属性并返回InputData对象。

如果InputData.DataTable列中没有列名称命名为"日期/时间",那么我们再次弹出错误消息框,并循环回到while循环的乞求。

需要注意的是,如果文件未通过列数测试或名为Date/Time的列测试……那么与您的问题一样,InputData.DataTable仍然有数据。这在这里是可以的,因为当我们循环回到while循环的乞求时,我们将重新初始化一个"新的"InputData对象。

最后,您没有显示InputData类,但它似乎至少有两(2)个属性…1)一个stringFilePath和2)一个名为DataTableDataTable???这看起来很奇怪而且不明确…我已经将InputData对象的DataTable属性重命名为DT。同样的"歧义"也适用于InputData变量,我已将其更改为TempInputData

由于每次用户选择BAD文件时,代码可能"潜在地"创建大量InputData对象,因此我在InputData类中实现了IDisposable接口。这样,我们就可以在using语句中使用这个类,并正确地处理代码创建的未使用的InputData对象。我希望我已经正确地执行了这一点。

public class InputData : IDisposable {
public DataTable DT;
public string FilePath;
private bool isDisposed;
public InputData() {
DT = new DataTable();
FilePath = "";
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) {
if (isDisposed) {
return;
}
if (disposing) {
DT?.Dispose();
FilePath = null;
}
isDisposed = true;
}
}

private InputData GetInputDataFromSCV() {
InputData TempInputData;
OpenFileDialog OFDReader;
string initialDirectory = @"D:TestCSV";
DialogResult result;
CsvConfiguration config = new CsvConfiguration(CultureInfo.InvariantCulture) {
Delimiter = ";",
IgnoreBlankLines = true,
MissingFieldFound = null,
BadDataFound = null
};
CsvReader csv;
CsvDataReader DataRead;
StreamReader readertmp;
FileInfo fi;
while (true) {
using (TempInputData = new InputData()) {
using (OFDReader = new OpenFileDialog()) {
OFDReader.Filter = "CSV files|*.csv;";
OFDReader.InitialDirectory = initialDirectory;
result = OFDReader.ShowDialog();
if (result == DialogResult.OK) {
fi = new FileInfo(OFDReader.FileName);
if (fi.Length != 0) {
using (readertmp = new StreamReader(OFDReader.FileName)) {
csv = new CsvReader(readertmp, config);
DataRead = new CsvDataReader(csv);
TempInputData.DT.Load(DataRead);
if (TempInputData.DT.Columns.Count > 2) {
foreach (DataColumn column in TempInputData.DT.Columns) {
if (column.ColumnName == "Date/Time") {
TempInputData.FilePath = OFDReader.FileName;
return TempInputData;
}
}
// if we get here we know a column named "Date/Time" was NOT found
MessageBox.Show("Data has no DataPoints !", "Wrong Data", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
else {
MessageBox.Show("Data has less than 2 columns?", "Wrong Data", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
}
else {
MessageBox.Show("File is empty!", "Wrong Data", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
else {
if (result == DialogResult.Cancel) {
return new InputData();
}
}
}
}
}
}

我希望这是有意义和帮助的。

很抱歉给您带来不便。有时我真的把它弄得太复杂了。我现在已经解决了如下问题:

if (result == DialogResult.Cancel)
{
if (inputDataHistory.Loadcount != 0)
{
TempInputData.FilePath = inputDataHistory.FilePathCache;
TempInputData.LineCounter = inputDataHistory.LinecounterCache;

var reader = new StreamReader(TempInputData.FilePath);
for (int i = 0; i < TempInputData.LineCounter; i++)
{
reader.ReadLine();
}
csv = new CsvReader(reader, config);
DataRead = new CsvDataReader(csv);
TempInputData.DT.Load(DataRead);
TempInputData.IsDisposed = true;
return TempInputData;
}
else
{
return new InputData();
}

我不知道这是否是最有效的解决方案,但我之前曾将关键变量读入另一个类中。取消时使用这些选项来重新读取之前的文件。

相关内容

最新更新