c# -文本文件与矩阵-除所有条目
本文关键字:文本 文件 | 更新日期: 2023-09-27 18:12:18
我有一个具有1122 x 1122降水测量矩阵的文本文件。每次测量用4位十进制数字表示。示例行如下所示:
0.0234 0.0023 0.0123 0.3223 0.1234 0.0032 0.1236 0.0000 ....
我需要这个相同的文本文件,但所有的值除以6。(我必须对920个这样的文件这样做....)
我设法做到了这一点,但毫无疑问以一种极其无效和耗尽内存的方式:
- 我一个一个地打开文本文件,逐行读取每个文本文件
- 我将每行拆分为字符串数组,其中单独的值作为成员
- 我遍历数组,将每个值转换为double,除以6并将结果转换回字符串,用4个十进制数字格式化并存储为新字符串数组中的成员。
- 我将数组连接回一行 我把这行写入一个新的文本文件。
- 瞧(大约一个小时后…)我有920个新的文本文件。
我相信有一种更快更专业的方法来做到这一点。我看了无数关于黑客帝国的网站。分歧,但没有看到(或理解)这个问题的解决方案。任何帮助将不胜感激!以下是用于每个文件的代码片段:
foreach (string inputline in inputfile)
{
int count = 0;
string[] str_precip = inputline.Split(' '); // holds string measurements
string[] str_divided_precip = new string[str_precip.Length]; // will hold string measurements divided by divider (6)
foreach (string measurements in str_precip)
{
str_divided_precip[count] = ((Convert.ToDouble(measurements)) / 6).ToString("F4", CultureInfo.CreateSpecificCulture("en-US"));
count++;
}
string divline = string.Join(" ", str_divided_precip);
using (System.IO.StreamWriter newfile = new System.IO.StreamWriter(@"asc_files'divfile.txt", true))
{
newfile.WriteLine(divline);
}
}
假设文件格式良好,您应该能够每次处理一个字符,而无需创建任何数组或进行任何复杂的字符串解析。
下面的代码片段展示了通用的方法:
string s = "12.4567 0.1234'n"; // just an example
decimal d = 0;
foreach (char c in s)
{
if (char.IsDigit(c))
{
d *= 10;
d += c - '0';
}
else if (c == ' ' || c == ''n')
{
d /= 60000; // divide by 10000 to get 4dps; divide by 6 here too
Console.Write(d.ToString("F4"));
Console.Write(c);
d = 0;
}
else {
// no special processing needed as long as input file always has 4dp
Debug.Assert(c == '.');
}
}
显然,您将写入(缓冲的)文件流而不是控制台。
你也许可以推出自己的更快版本的ToString("F4")
,但我怀疑它会对时间产生重大影响。但是,如果您可以使用这种方法避免为输入文件的每一行创建一个新数组,我希望它会产生很大的不同。(相反,每个文件一个数组作为缓冲写入器是值得的,特别是如果它从一开始就声明得足够大。)
编辑 (萨尼辛格Huttunen )
很抱歉编辑了你的帖子,但你绝对是正确的。
在这种情况下,定点算法将提供一个显著的改进。
在引入StreamReader
(~10%的改进),float
(另一个~35%的改进)和其他改进(又一个~20%的改进)(见评论)之后,这种方法需要~12分钟(我的回答中的系统规格):
public void DivideMatrixByScalarFixedPoint(string inputFilname, string outputFilename)
{
using (var inFile = new StreamReader(inputFilname))
using (var outFile = new StreamWriter(outputFilename))
{
var d = 0;
while (!inFile.EndOfStream)
{
var c = (char) inFile.Read();
if (c >= '0' && c <= '9')
{
d = (d * 10) + (c - '0');
}
else if (c == ' ' || c == ''n')
{
// divide by 10000 to get 4dps; divide by 6 here too
outFile.Write((d / 60000f).ToString("F4", CultureInfo.InvariantCulture.NumberFormat));
outFile.Write(c);
d = 0;
}
}
}
}
您打开/关闭每个值的输出,我认为我们可以做得更好!只需将其替换为以下代码:
using (System.IO.StreamWriter newfile = new System.IO.StreamWriter(@"asc_files'divfile.txt", true))
{
foreach (string inputline in inputfile)
{
int count = 0;
foreach (string measurements in inputline.Split(' '))
{
newfile.Write((Convert.ToDouble(measurements) / 6).ToString("F4", CultureInfo.CreateSpecificCulture("en-US")));
if (++count < 1122)
{
newfile.Write(" ");
}
}
newfile.WriteLine();
}
}
对于读取部分,您可能希望使用ReadLine()
一次读取一行,而不是在一个巨大的块中读取整个文件,然后将其拆分到内存中。这种流方法将大大减少内存分配,并基于硬件(您有多少内存,您的磁盘有多快(HDD?SSD?)可以以一种明智的方式提高性能!
让我知道它现在是如何工作的,我很好奇!
数学。. NET Numerics对于这类操作很有用。
应该速度快,占用内存少。
using MathNet.Numerics.Data.Text;
using MathNet.Numerics.LinearAlgebra;
public void DivideMatrixByScalar(string inputFilename, string outputFilename, double scalar)
{
Matrix<double> matrix;
using (var sr = new StreamReader(inputFilename))
{
matrix = DelimitedReader.Read<double>(sr, false, "''s", false, CultureInfo.InvariantCulture.NumberFormat);
}
// Divide all values with the scalar.
matrix = matrix.Divide(scalar);
using (var sw = new StreamWriter(outputFilename))
{
DelimitedWriter.Write(sw, matrix, " ", null, "0.0000", CultureInfo.InvariantCulture.NumberFormat);
}
}
浏览920个1122x1122双值文件的时间:~43分钟。
内存占用:最大129mb,平均59mb。
CPU使用率:最高20%,平均18%
结论是,这是非常繁重的I/O,这是占用大部分时间。
SSD或更好的raidssd会加快速度。
系统规格
硬盘WD20EARS 5400 RPM
24GB DDR3 @ 2133 MHz