如何创建由开始和停止按钮控制的 CSV 文件
How to create a CSV file controlled by Start- and Stop-Buttons
我想设计一个 windows 表单应用程序,将我的数据(大约 70k 个值)放在一个数组中,以便在最短的时间内将其传输到 csv 文件,csv 文件路径和名称通过 windows 表格申请。
数据传输仅在按下第一个名为开始的按钮时完成,当按下第二个按钮停止时,无论值的数量如何,数据传输都应该停止。
代码如下:
public static void Write(double[] data, string outputPath)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < data.GetLength(0); i++)
{
sb.AppendLine(string.Join(",", data[i]));
}
File.WriteAllText(outputPath, sb.ToString());
}
这是我正在使用的函数,它具有数组变量 dscaledData。我想从此数组中获取值并将其存储在 csv 文件中
Write(dScaledData, @csvadd);
@部分显示带名称的csv路径
存储在 dscaledData 变量中的数据值非常大。 70k
您想使用停止按钮以某种方式中断您的写入循环。但实际上,它将保持 运行ning 并确实阻塞 ui 线程。所以你需要运行在不同的线程中写循环..
在 .NET 中有多种方法可以做到这一点,例如Task.Factory
、threads
或 BackgroundWorker
.
这是一个使用 Task.Factory
:
的简单但有效的实现
我们创建了一个 class 级别变量 stopped
,我们从 Buttons
设置和重置它。还有其他更复杂的方法可以取消 Task
、see here,但这足以达到目的..
对于我的测试,我还创建了虚拟数据..
private void cb_Stop_Click(object sender, EventArgs e)
{ stopped = true; // this will be checked in our output loop }
private void cb_Start_Click(object sender, EventArgs e)
{
stopped = false;
// create my test data..
List<double> data = new List<double>();
for (int i = 0; i < 10000000; i++) data.Add((i+ 1d) / i * 1d );
string filename = "D:\xxxxx.txt";
// this is an optional callback to provide feedback.
// It obviuosly slows things down greatly..
Action<int> callback = (value) => st_lines.Invoke(new Action(()
=> st_lines.Text = value + " lines written.."));
// now we start the write loop in another task..
// ..passing in our data and (optinally) the callback
Task myFirstTask = Task.Factory.StartNew(()
=> Write(data.ToArray(), filename, callback));
}
static bool stopped = true; // our flag
public static void Write(double[] data, string outputPath, Action<int> aCallback)
{ // your write loop
StringBuilder sb = new StringBuilder();
for (int i = 0; i < data.GetLength(0); i++)
{
if (stopped) break; // our new test
sb.AppendLine(string.Join(",", data[i]));
aCallback( i ); // optional callback
}
File.WriteAllText(outputPath, sb.ToString());
}
关于时间情况的几点说明:
将 70k 行添加到 StringBuilder
应该不会花费很多时间。看看我的代码:我必须将测试数据增加到 10M 行才能按下机器上的停止按钮!
也许数据的创建是瓶颈,或者是写入磁盘..?或者也许没有瓶颈?在决定实施之前,您应该了解这一点!
如果其他部分被识别为瓶颈,您将不得不相应地更改代码以打破其他循环..
我添加了回调机制,因此您可以看到如何将有关任务进度的信息带回 UI 线程。它应该可以帮助您的用户决定停止任务是否有意义..
但是添加回调会使事情变慢很多,所以你可以这样做,而不是为每一行都调用它:if (i % 1000 == 0 ) aCallback(i);
并且只更新进度标签(或 ProgressBar
, 当然) 每 1000 行.
为此您需要使用 BackgroundWorker。这正是您所需要的。
不要使用 File.WriteAllText,但在工作线程中使用 File.WriteLine。
在我看来 70k 行不是很多,如果你的数据量在未来不会大幅增长,人类将无法 start/stop 除非你人为地暂停 writer thread with a Thread.Sleep()
我想设计一个 windows 表单应用程序,将我的数据(大约 70k 个值)放在一个数组中,以便在最短的时间内将其传输到 csv 文件,csv 文件路径和名称通过 windows 表格申请。
数据传输仅在按下第一个名为开始的按钮时完成,当按下第二个按钮停止时,无论值的数量如何,数据传输都应该停止。
代码如下:
public static void Write(double[] data, string outputPath)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < data.GetLength(0); i++)
{
sb.AppendLine(string.Join(",", data[i]));
}
File.WriteAllText(outputPath, sb.ToString());
}
这是我正在使用的函数,它具有数组变量 dscaledData。我想从此数组中获取值并将其存储在 csv 文件中
Write(dScaledData, @csvadd);
@部分显示带名称的csv路径 存储在 dscaledData 变量中的数据值非常大。 70k
您想使用停止按钮以某种方式中断您的写入循环。但实际上,它将保持 运行ning 并确实阻塞 ui 线程。所以你需要运行在不同的线程中写循环..
在 .NET 中有多种方法可以做到这一点,例如Task.Factory
、threads
或 BackgroundWorker
.
这是一个使用 Task.Factory
:
我们创建了一个 class 级别变量 stopped
,我们从 Buttons
设置和重置它。还有其他更复杂的方法可以取消 Task
、see here,但这足以达到目的..
对于我的测试,我还创建了虚拟数据..
private void cb_Stop_Click(object sender, EventArgs e)
{ stopped = true; // this will be checked in our output loop }
private void cb_Start_Click(object sender, EventArgs e)
{
stopped = false;
// create my test data..
List<double> data = new List<double>();
for (int i = 0; i < 10000000; i++) data.Add((i+ 1d) / i * 1d );
string filename = "D:\xxxxx.txt";
// this is an optional callback to provide feedback.
// It obviuosly slows things down greatly..
Action<int> callback = (value) => st_lines.Invoke(new Action(()
=> st_lines.Text = value + " lines written.."));
// now we start the write loop in another task..
// ..passing in our data and (optinally) the callback
Task myFirstTask = Task.Factory.StartNew(()
=> Write(data.ToArray(), filename, callback));
}
static bool stopped = true; // our flag
public static void Write(double[] data, string outputPath, Action<int> aCallback)
{ // your write loop
StringBuilder sb = new StringBuilder();
for (int i = 0; i < data.GetLength(0); i++)
{
if (stopped) break; // our new test
sb.AppendLine(string.Join(",", data[i]));
aCallback( i ); // optional callback
}
File.WriteAllText(outputPath, sb.ToString());
}
关于时间情况的几点说明:
将 70k 行添加到 StringBuilder
应该不会花费很多时间。看看我的代码:我必须将测试数据增加到 10M 行才能按下机器上的停止按钮!
也许数据的创建是瓶颈,或者是写入磁盘..?或者也许没有瓶颈?在决定实施之前,您应该了解这一点!
如果其他部分被识别为瓶颈,您将不得不相应地更改代码以打破其他循环..
我添加了回调机制,因此您可以看到如何将有关任务进度的信息带回 UI 线程。它应该可以帮助您的用户决定停止任务是否有意义..
但是添加回调会使事情变慢很多,所以你可以这样做,而不是为每一行都调用它:if (i % 1000 == 0 ) aCallback(i);
并且只更新进度标签(或 ProgressBar
, 当然) 每 1000 行.
为此您需要使用 BackgroundWorker。这正是您所需要的。
不要使用 File.WriteAllText,但在工作线程中使用 File.WriteLine。
在我看来 70k 行不是很多,如果你的数据量在未来不会大幅增长,人类将无法 start/stop 除非你人为地暂停 writer thread with a Thread.Sleep()