none
大型文字檔的讀取 RRS feed

  • 問題

  • 我讀取檔案, 並將之post在 textbox 之中!

    小型的檔案, 數百行, 約2-3秒出來!

    大型的檔案, 數千行(我的例子是5000行左右), 就當了! 完全不會動!

    可是 notepad 可以在一秒之內就完成了!是不是那裏有什麼問題?

    private void btStart_Click(object sender, EventArgs e){

         string[] beforeModify = File.ReadAllLines(Path.Combine(path, fullName));

         foreach(string str in beforeModify)
        {  
            addInfo(str);
        }

    }

    private void addInfo(string str)

    {
           tbFileDetail.Text = str + Environment.NewLine + tbFileDetail.Text;
    }


    謝謝指教!

    2019年11月11日 上午 08:07

解答

  • var beforeModify = File.ReadAllLines(Path.Combine(path, fullName));
    beforeModify.Reverse();
    var strBuilder = new StringBuilder();
    foreach (string str in beforeModify)
    {
        strBuilder.AppendLine(str);
    }
    tbFileDetail.Text = strBuilder.ToString();
    • 已標示為解答 GaryChiang 2019年11月12日 上午 07:00
    • 已編輯 [-] 2019年11月14日 上午 01:25
    2019年11月11日 上午 09:35
  • 1.直接讀成 byte[] 陣列

    2.用 Encoding 轉換成字串

    3.直接指給 TextBox.Text

    不須處理任何換行與字串相加,這樣最快。


    不精確的問法,就會得到隨便猜的答案;自己都不肯花時間好好描述問題,又何必期望網友會認真回答?

    • 已標示為解答 GaryChiang 2019年11月12日 上午 07:00
    2019年11月11日 下午 02:43
  • 您可以這樣做:

    private void btStart_Click(object sender, EventArgs e)
    {
         	string[] beforeModify = File.ReadAllLines(Path.Combine(path, fullName));
         	foreach(string str in beforeModify)
         	{  
            	addInfo(str);
         	}
            tbFileDetail.Text=sb.ToString();
    }
    StringBuilder sb=new StringBuilder();
    private void addInfo(string str)
    {
     	sb.Append(str);
    	sb.Append(Environment.NewLine);      
    	sb.Append(tbFileDetail.Text);
    }

    • 已標示為解答 GaryChiang 2019年11月12日 上午 07:00
    2019年11月11日 下午 10:24

所有回覆

  • var beforeModify = File.ReadAllLines(Path.Combine(path, fullName));
    beforeModify.Reverse();
    var strBuilder = new StringBuilder();
    foreach (string str in beforeModify)
    {
        strBuilder.AppendLine(str);
    }
    tbFileDetail.Text = strBuilder.ToString();
    • 已標示為解答 GaryChiang 2019年11月12日 上午 07:00
    • 已編輯 [-] 2019年11月14日 上午 01:25
    2019年11月11日 上午 09:35
  • 1.直接讀成 byte[] 陣列

    2.用 Encoding 轉換成字串

    3.直接指給 TextBox.Text

    不須處理任何換行與字串相加,這樣最快。


    不精確的問法,就會得到隨便猜的答案;自己都不肯花時間好好描述問題,又何必期望網友會認真回答?

    • 已標示為解答 GaryChiang 2019年11月12日 上午 07:00
    2019年11月11日 下午 02:43
  • 您可以這樣做:

    private void btStart_Click(object sender, EventArgs e)
    {
         	string[] beforeModify = File.ReadAllLines(Path.Combine(path, fullName));
         	foreach(string str in beforeModify)
         	{  
            	addInfo(str);
         	}
            tbFileDetail.Text=sb.ToString();
    }
    StringBuilder sb=new StringBuilder();
    private void addInfo(string str)
    {
     	sb.Append(str);
    	sb.Append(Environment.NewLine);      
    	sb.Append(tbFileDetail.Text);
    }

    • 已標示為解答 GaryChiang 2019年11月12日 上午 07:00
    2019年11月11日 下午 10:24
  • 也可以試試直接 ReadAllText , 一次讀完整份文件,回傳整篇的字串

    參考 File.ReadAllText 方法


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。 https://skilltree.my/

    2019年11月12日 下午 08:54
    版主
  • 也可以試試直接 ReadAllText , 一次讀完整份文件,回傳整篇的字串

    參考 File.ReadAllText 方法


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。 https://skilltree.my/

    从他这行代码(tbFileDetail.Text = str + Environment.NewLine + tbFileDetail.Text;)的意思上看,他是要反转全部内容,第一行放到最后,最后一行放到最前面,所以才自己写了这么个函数。
    2019年11月13日 上午 02:46
  • 从他这行代码(tbFileDetail.Text = str + Environment.NewLine + tbFileDetail.Text;)的意思上看,他是要反转全部内容,第一行放到最后,最后一行放到最前面,所以才自己写了这么个函数。
    對喔,這我疏忽了。只注意到 notepad 讀取速度,沒想到還需要反轉。

    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。 https://skilltree.my/


    2019年11月13日 上午 04:36
    版主
  • 如果有反轉需求

    2.1 陣列字串 = 字串.split(換行字元)

    2.2 Array.Reverse(陣列字串)

    取代3

    3. TextBox.Text = String.Join(換行字元, 陣列字串)

    不用迴圈與字串相加,也是比較快。

    註:實測 StringBuilder 可能會比 String.Join 快,可能而已。


    不精確的問法,就會得到隨便猜的答案;自己都不肯花時間好好描述問題,又何必期望網友會認真回答?

    2019年11月13日 下午 02:56
  • 如果有反轉需求

    2.1 陣列字串 = 字串.split(換行字元)

    2.2 Array.Reverse(陣列字串)

    取代3

    3. TextBox.Text = String.Join(換行字元, 陣列字串)

    不用迴圈與字串相加,也是比較快。

    註:實測 StringBuilder 可能會比 String.Join 快,可能而已。


    不精確的問法,就會得到隨便猜的答案;自己都不肯花時間好好描述問題,又何必期望網友會認真回答?

    string.join:

    https://referencesource.microsoft.com/#mscorlib/system/string.cs,06d13c9cb8b83f5d

    stringbuilder:

    https://referencesource.microsoft.com/#mscorlib/system/text/stringbuilder.cs,e8eaef3c361184bc,references

    最终都是调用同一个函数:

    extern private unsafe static void __Memmove(byte* dest, byte* src, nuint len);

    因此性能应该差不多,stringbuilder如果预先分配好缓冲区,性能显然会进一步提高。当然如果这点性能差异都很重要,那还不如直接自己写一个unsafe函数,性能毫无疑问会超越所有.net库函数,因为省掉了各种中间环节,只是我想不出有什么情况会需要如此极致的性能。

    不过为了一探究竟,还是写了以下代码来测试。

    using System;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;
    using System.Text;
    namespace ConsoleApp1
    {
        class Program
        {
            static void Main(string[] args)
            {
                try
                {
                    Console.WriteLine("读取一百万行的测试文件(为避免干扰每次只测试一种方案)【1.基准读取测试、2-5.读取并反转、0.生成测试文件】");
                    var watch = new Stopwatch();
                    switch (Console.ReadKey().Key)
                    {
                        case ConsoleKey.D1:
                        case ConsoleKey.NumPad1:
                            Test("直接打开文件用时:", watch, () => { File.ReadAllText(@"d:\test.txt", Encoding.UTF8); });
                            break;
                        case ConsoleKey.D2:
                        case ConsoleKey.NumPad2:
                            Test("反转并使用自定义 Unsafe 用时:", watch, () => { Load_Unsafe(@"d:\test.txt", Encoding.UTF8); });
                            break;
                        case ConsoleKey.D3:
                        case ConsoleKey.NumPad3:
                            Test("反转并使用 StringBuilder 用时:", watch, () => { Load_StringBuilder(@"d:\test.txt", Encoding.UTF8); });
                            break;
                        case ConsoleKey.D4:
                        case ConsoleKey.NumPad4:
                            Test("反转并使用预分配缓冲区 StringBuilder 用时:", watch, () => { Load_StringBuilder_Preprocessing(@"d:\test.txt", Encoding.UTF8); });
                            break;
                        case ConsoleKey.D5:
                        case ConsoleKey.NumPad5:
                            Test("反转并使用 String.Join 用时:", watch, () => { Load_StringJoin(@"d:\test.txt", Encoding.UTF8); });
                            break;
                        case ConsoleKey.D0:
                        case ConsoleKey.NumPad0:
                            CreateTestFile();
                            return;
                        default:
                            return;
                    }
                }
                catch (Exception err)
                {
                    Console.WriteLine(err.Message);
                }
                Console.ReadKey();
            }
            static void CreateTestFile()
            {
                var str = new StringBuilder(70000000);
                str.AppendLine("Begin");
                for (var i = 0; i != 1000000; ++i)
                {
                    str.AppendLine("abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
                }
                str.Append("End");
                File.WriteAllText(@"d:\test.txt", str.ToString(), Encoding.UTF8);
            }
            static void Test(string msg, Stopwatch watch, Action action)
            {
                watch.Restart();
                action();
                watch.Stop();
                var ts = watch.Elapsed;
                Console.WriteLine(msg + string.Format("{0:00}:{1:00}:{2:00}.{3:000}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds));
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }
            static string Load_StringJoin(string path, Encoding encoding)
            {
                return string.Join(Environment.NewLine, File.ReadAllLines(path, encoding).Reverse());
            }
            static string Load_StringBuilder(string path, Encoding encoding)
            {
                var lines = File.ReadAllLines(path, encoding).Reverse();
                var str = new StringBuilder();
                foreach (string line in lines) str.AppendLine(line);
                return str.ToString();
            }
            static string Load_StringBuilder_Preprocessing(string path, Encoding encoding)
            {
                var lines = File.ReadAllLines(path, encoding).Reverse();
                var linebreak_length = Environment.NewLine.Length;
                var length = 0;
                foreach (string line in lines)
                {
                    length += line.Length;
                    length += linebreak_length;
                }
                var str = new StringBuilder(length);
                foreach (string line in lines) str.AppendLine(line);
                return str.ToString();
            }
            static unsafe string Load_Unsafe(string path, Encoding encoding)
            {
                var text = File.ReadAllText(path, encoding);
                var text_length = text.Length;
                if (text_length == 0) return null;
                var buffer = new string((char)0, text_length);
                fixed (char* source_pointer = text)
                {
                    fixed (char* target_pointer = buffer)
                    {
                        var linebreak = '\n';
                        var chr = source_pointer;
                        var text_begin = 0;
                        var text_end = 0;
                        var target_index = text_length;
                        var line_length = 0;
                        char* source;
                        char* target;
                        int index;
                        for (var text_index = 0; text_index != text_length; ++text_index, ++chr)
                        {
                            ++text_end;
                            if (*chr == linebreak)
                            {
                                line_length = text_end - text_begin;
                                if (target_index == text_length) line_length -= source_pointer[text_length - 1] == linebreak ? 0 : Environment.NewLine.Length;
                                target_index -= line_length;
                                source = source_pointer + text_begin;
                                target = target_pointer + target_index;
                                for (index = 0; index != line_length; ++index, ++source, ++target) *target = *source;
                                text_begin = text_end;
                            }
                        }
                        if (text_begin != text_end)
                        {
                            line_length = text_end - text_begin;
                            source = source_pointer + text_begin;
                            target = target_pointer;
                            for (index = 0; index != line_length; ++index, ++source, ++target) *target = *source;
                            foreach (var temp in Environment.NewLine)
                            {
                                *target = temp;
                                ++target;
                            }
                        }
                    }
                }
                return buffer;
            }
        }
    }

    2019年11月14日 上午 05:30
  • 謝謝大家指教!

    大家都太專業了!

    後來發現, 不要一直不停更新 textbox的資料, 而是在記憶體處理這些工作, 再一次送給 textbox, 這樣速度就快很多了!

    感謝!

    2019年11月14日 上午 09:02