代码之家  ›  专栏  ›  技术社区  ›  Ali Kanat

如何在C语言中高效地将文本文件转换为二进制文件#

  •  0
  • Ali Kanat  · 技术社区  · 7 年前

    我检查了几种将文本文件转换为二进制文件的方法,并在这里找到了一些答案。然而,由于Unity.NET的兼容性,它们中的大多数都让我感到困惑,我也对如何将文本转换为二进制的结构感到困惑。

    我有一个文本文件(导出的点云),它保存了点在三维空间中的位置和颜色信息,如下所示:

    X Y Z colorvalues -0.680891 -90.6809 0 204 204 204 255

    我读这个是为了在运行时用这样的脚本创建网格:

     string[] buffer;
    
        for (int i = 0; i < Area.nPoints; i++)
        {
            buffer = sr.ReadLine().Split();
    
            Area.AddPoint(new Vector3(float.Parse(buffer[0]), 
            float.Parse(buffer[1]), float.Parse(buffer[2])));
        }
    

    这是可行的,但因为我阅读行并拆分它们,所以速度很慢,我的文本文件中有大约7500万行(点)。我发现我可以把它转换成二进制,读起来会更快,而且速度也快得多。但是,现在转换为二进制部分非常慢,我想问你我转换的方式。

    void WriteValues()
    {
        string[] buffer;
    
        for (int i = 0; i < numPoints; i++)
        {
            buffer = sr.ReadLine().Split();
            for (int j = 0; i < 3; i++)
            {
                wr.Write(float.Parse(buffer[j]));
            }           
        }        
        wr.Close();
    }
    

    然后我读了它 BinaryReader.ReadSingle() 但这比直接从文本中阅读要花更多的时间,因为我再次阅读并拆分了行。

    我的问题是,我是否可以读,比如说接下来的1000行,缓冲它,然后写,而不是读每一行?会有什么不同吗?如果是这样的话,我怎么能每1000行使用一次流呢?

    同样,当我将一行转换为二进制时,如何在不拆分字符串的情况下读取行中的每个浮点值?提前感谢您的帮助!

    我正试图用增强现实来可视化我手机中的点云。所以我想做扫描,导出点云,导入到Unity,通过使用这些点创建网格而不进行三角测量,但是使用我的初始方法导入它需要15-18分钟。转换成二进制文件后,只需不到3分钟就可以了。但是,转换为二进制文件需要花费大量时间:)

    3 回复  |  直到 7 年前
        1
  •  1
  •   Keith Nicholas    7 年前

    因此,一种相当快速的读取方法是使用缓冲文件流。如果不进行浮点分析,在我的机器上读取需要14秒….74秒的ish和float解析(我只是总结了一下,因为我没有统一性可玩)

    var sw = new Stopwatch();
    sw.Start();
    double sum = 0;
    var fs = new FileStream("demo.txt", FileMode.Open, FileAccess.Read);
    using (var bs = new BufferedStream(fs))
    using (var r = new StreamReader(bs))
    {
        r.ReadLine();
        while (!r.EndOfStream)
        {
            var l = r.ReadLine();
            var split = l.Split();
            var x = float.Parse(split[0]);
            var y = float.Parse(split[1]);
            var z=float.Parse(split[2]);
            sum += x + y + z;
        }
    }
    sw.Stop();
    Console.WriteLine(sw.ElapsedMilliseconds / 1000M);
    Console.WriteLine(sum);
    

    出于兴趣,我还修改了代码,将数据作为一个浮动流(三元组)写入。

    读入

    var sw = new Stopwatch();
    sw.Start();
    double sum = 0;
    var fs = new FileStream("demo.bin", FileMode.Open, FileAccess.Read);
    using (var bs = new BufferedStream(fs))
    using (var r = new BinaryReader(bs))
    {
        for (int i = 0; i < 75000000; i++)
        {
            var x = r.ReadSingle();
            var y = r.ReadSingle();
            var z=r.ReadSingle();
            sum += x + y + z;
        }
    }
    sw.Stop();
    Console.WriteLine(sw.ElapsedMilliseconds / 1000M);
    Console.WriteLine(sum);
    

    大约需要9秒

    为了完整起见,我使用以下代码生成演示文件。

       var random = new Random();
        File.WriteAllText("demo.txt", "X         Y        Z colorvalues\r\n");
        using (var fs = new FileStream("demo.bin", FileMode.Create, FileAccess.Write, FileShare.None))
        using (var bw = new BinaryWriter(fs))
        using (var writer = File.AppendText("demo.txt"))
        {
            for (int i = 0; i < 75000000; i++)
            {
                var x = (float) random.NextDouble() * 200;
                var y = (float) random.NextDouble() * 200;
                var z = (float) random.NextDouble() * 200;
                var c = Enumerable.Range(0, 4).Select(n => random.Next(0, 255)).ToArray();
                writer.WriteLine($"{x} {y} {z} {c[0]} {c[1]} {c[2]} {c[3]}");
                bw.Write(x);
                bw.Write(y);
                bw.Write(z);
            }
    }
    
        2
  •  1
  •   Adammak    7 年前

    这可能是个愚蠢的问题,但为什么不直接扫描并保存到二进制或.ply文件中呢?甚至扫描并保存到网格或某些体素化样式网格中

    您还可以查找中使用的方法 this 特别是项目 PlyImporter.cs

        3
  •  0
  •   Leo    7 年前

    如果读取速度很慢,那么读取、写入不同的文件格式,然后再从该文件读取数据的速度会更慢。你只是在给已经很慢的事情添加更多的动作…也许你应该看看如何改变你从文本文件中读取的方式。

    如果您不熟悉如何使用内置库在C中进行序列化/反序列化,则应首先阅读以下内容: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/serialization/

    下面是一个演示如何实现二进制序列化的链接: https://docs.microsoft.com/en-us/dotnet/api/system.runtime.serialization.formatters.binary.binaryformatter?view=netframework-4.7.2

    但是,如果不编写初始文件,只需编写一个自定义反序列化程序(这基本上就是您所做的——不需要实现相关的.NET模式)。也许可以尝试使用 BufferedStream 看看是否有帮助,比如:

    using (FileStream fs = File.Open(fileName, ..... ))
    using (BufferedStream bs = new BufferedStream(fs))
    using (StreamReader sr = new StreamReader(bs))
    {
            string s;
            while ((s = sr.ReadLine()) != null)
            {
                //your code   
            }
    }
    

    另外,值得一看这个库,它可以帮助您完成此任务: FileHelpers -看看这个例子: https://www.filehelpers.net/example/QuickStart/ReadFileDelimited/