簡體   English   中英

從文件C#中讀取double值

[英]Read double value from a file C#

我有一個txt文件,其格式為:

0.32423 1.3453 3.23423
0.12332 3.1231 9.23432432
9.234324234 -1.23432 12.23432
...

每行具有三個double值。 此文件中有10000多行。 我可以使用ReadStream.ReadLine並使用String.Split,然后將其轉換。 我想知道有沒有更快的方法可以做到這一點。

最好的祝福,

在這里, StreamReader.ReadLineString.SplitDouble.TryParse聽起來是一個不錯的解決方案。
無需改進。

您可能會執行一些微優化,但是建議的方法聽起來很簡單。

10000行不應該花費很長時間-您是否嘗試過,發現實際上遇到了性能問題? 例如,這里有兩個簡短的程序-一個創建一個10,000行的文件,另一個讀取它:

CreateFile.cs:

using System;
using System.IO;

public class Test
{
    static void Main()
    {
        Random rng = new Random();
        using (TextWriter writer = File.CreateText("test.txt"))
        {
            for (int i = 0; i < 10000; i++)
            {
                writer.WriteLine("{0} {1} {2}", rng.NextDouble(),
                                 rng.NextDouble(), rng.NextDouble());
            }
        }
    }
}

ReadFile.cs:

using System;
using System.Diagnostics;
using System.IO;
using System.Linq;

public class Test
{
    static void Main()
    {   
        Stopwatch sw = Stopwatch.StartNew();
        using (TextReader reader = File.OpenText("test.txt"))
        {
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                string[] bits = line.Split(' ');
                foreach (string bit in bits)
                {
                    double value;
                    if (!double.TryParse(bit, out value))
                    {
                        Console.WriteLine("Bad value");
                    }
                }
            }
        }
        sw.Stop();
        Console.WriteLine("Total time: {0}ms",
                          sw.ElapsedMilliseconds);
    }
}

在我的上網本上(公認有SSD),只需要82毫秒即可讀取文件。 我建議這可能不是問題:)

我建議您一次閱讀所有行

string[] lines = System.IO.File.ReadAllLines(fileName);

這樣可以確保以最高效率完成I / O。 您將必須進行測量(配置文件),但我希望轉換花費的時間會少得多。

該解決方案要慢一些(請參閱最后的基准測試),但它讀起來更好。 它還應該提高內存效率,因為此時僅緩沖當前字符(而不是整個文件或行)。

讀取數組是該閱讀器的一項附加功能,它假定數組的大小始終始終是int值。

IParsable是另一個功能,可輕松實現各種類型的Parse方法。

class StringSteamReader {
    private StreamReader sr;

    public StringSteamReader(StreamReader sr) {
        this.sr = sr;
        this.Separator = ' ';
    }

    private StringBuilder sb = new StringBuilder();
    public string ReadWord() {
        eol = false;
        sb.Clear();
        char c;
        while (!sr.EndOfStream) {
            c = (char)sr.Read();
            if (c == Separator) break;
            if (IsNewLine(c)) {
                eol = true;
                char nextch = (char)sr.Peek();
                while (IsNewLine(nextch)) {
                    sr.Read(); // consume all newlines
                    nextch = (char)sr.Peek();
                }
                break;
            }
            sb.Append(c);
        }
        return sb.ToString();
    }

    private bool IsNewLine(char c) {
        return c == '\r' || c == '\n';
    }

    public int ReadInt() {
        return int.Parse(ReadWord());
    }

    public double ReadDouble() {
        return double.Parse(ReadWord());
    }

    public bool EOF {
        get { return sr.EndOfStream; }
    }

    public char Separator { get; set; }

    bool eol;
    public bool EOL {
        get { return eol || sr.EndOfStream; }
    }

    public T ReadObject<T>() where T : IParsable, new() {
        var obj = new T();
        obj.Parse(this);
        return obj;
    }

    public int[] ReadIntArray() {
        int size = ReadInt();
        var a = new int[size];
        for (int i = 0; i < size; i++) {
            a[i] = ReadInt();
        }
        return a;
    }

    public double[] ReadDoubleArray() {
        int size = ReadInt();
        var a = new double[size];
        for (int i = 0; i < size; i++) {
            a[i] = ReadDouble();
        }
        return a;
    }

    public T[] ReadObjectArray<T>() where T : IParsable, new() {
        int size = ReadInt();
        var a = new T[size];
        for (int i = 0; i < size; i++) {
            a[i] = ReadObject<T>();
        }
        return a;
    }

    internal void NextLine() {
        eol = false;
    }
}

interface IParsable {
    void Parse(StringSteamReader r);
}

可以這樣使用:

public void Parse(StringSteamReader r) {
    double x = r.ReadDouble();
    int y = r.ReadInt();
    string z = r.ReadWord();
    double[] arr = r.ReadDoubleArray();
    MyParsableObject o = r.ReadObject<MyParsableObject>();
    MyParsableObject [] oarr = r.ReadObjectArray<MyParsableObject>();
}

我做了一些基准測試,將StringStreamReader與其他已經提出的方法( StreamReader.ReadLineFile.ReadAllLines )進行了比較。 這是我用於基准測試的方法:

private static void Test_StringStreamReader(string filename) {
    var sw = new Stopwatch();
    sw.Start();
    using (var sr = new StreamReader(new FileStream(filename, FileMode.Open, FileAccess.Read))) {
        var r = new StringSteamReader(sr);
        r.Separator = ' ';
        while (!r.EOF) {
            var dbls = new List<double>();
            while (!r.EOF) {
                dbls.Add(r.ReadDouble());
            }
        }
    }
    sw.Stop();
    Console.WriteLine("elapsed: {0}", sw.Elapsed);
}

private static void Test_ReadLine(string filename) {
    var sw = new Stopwatch();
    sw.Start();
    using (var sr = new StreamReader(new FileStream(filename, FileMode.Open, FileAccess.Read))) {
        var dbls = new List<double>();

        while (!sr.EndOfStream) {
            string line = sr.ReadLine();
            string[] bits = line.Split(' ');
            foreach(string bit in bits) {
                dbls.Add(double.Parse(bit));
            }
        }
    }
    sw.Stop();
    Console.WriteLine("elapsed: {0}", sw.Elapsed);
}

private static void Test_ReadAllLines(string filename) {
    var sw = new Stopwatch();
    sw.Start();
    string[] lines = System.IO.File.ReadAllLines(filename);
    var dbls = new List<double>();
    foreach(var line in lines) {
        string[] bits = line.Split(' ');
        foreach (string bit in bits) {
            dbls.Add(double.Parse(bit));
        }
    }        
    sw.Stop();
    Console.WriteLine("Test_ReadAllLines: {0}", sw.Elapsed);
}

我使用了具有1.000.000行的雙精度值的文件(每行3個值)。 文件位於SSD磁盤上,每個測試在發布模式下重復了多次。 這些是結果(平均):

Test_StringStreamReader: 00:00:01.1980975
Test_ReadLine:           00:00:00.9117553
Test_ReadAllLines:       00:00:01.1362452

因此,如上所述, StringStreamReader比其他方法要慢一些。 對於10.000條線,性能約為(120ms / 95ms / 100ms)。

您的方法已經很好!

您可以編寫一個readline函數來返回一個double數組,並在其他程序中重用此函數,以改進它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM