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个这样的文件这样做....)

我设法做到了这一点,但毫无疑问以一种极其无效和耗尽内存的方式:

  1. 我一个一个地打开文本文件,逐行读取每个文本文件
  2. 我将每行拆分为字符串数组,其中单独的值作为成员
  3. 我遍历数组,将每个值转换为double,除以6并将结果转换回字符串,用4个十进制数字格式化并存储为新字符串数组中的成员。
  4. 我将数组连接回一行
  5. 我把这行写入一个新的文本文件。
  6. 瞧(大约一个小时后…)我有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);
        }
    } 

c# -文本文件与矩阵-除所有条目

假设文件格式良好,您应该能够每次处理一个字符,而无需创建任何数组或进行任何复杂的字符串解析。

下面的代码片段展示了通用的方法:

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