【问题标题】:Find all newlines in richtextbox在richtextbox中查找所有换行符
【发布时间】:2018-10-11 10:45:33
【问题描述】:

我正在开发一个自定义文本编辑器控件并遇到了这个问题。

我需要一个函数来获取文本中每个换行符“\n”的字符索引。 我已经有两种方法来完成这个:

private List<int> GetNewLineLocations()
    {
        var list = new List<int>();
        int ix = 0;
        foreach (var c in this.Text)
        {
            if (c == '\n') list.Add(ix);
            ix++;
        }
        Debug.WriteLine(ix);
        return list;
    }

还有:

private List<int> GetNewLineLocations()
    {
        var list = new List<int>();
        int ix = -1;

        for (int i = 0; i < this.Lines.Length; i++)
        {
            ix += Lines[i].Length;
            ix += 1;
            list.Add(ix);
        }

        return list;
    }

第一个解决方案确实有效,但在大约 40000 个字符的 Richtextbox 中输入的文本越多,速度越慢,但可以分布在很多行中,例如 20000 个。

第二个似乎更快,因为它循环更少,并且或多或少相同,但在 1000 行时显着减慢,无论它们包含多少文本。

代码当然需要快速运行并且不占用大量资源,这就是为什么我认为第二种解决方案会更好。

我的问题是:

  1. 哪种解决方案更好,为什么?

  2. 为什么第二种解决方案这么慢?

  3. 还有更好的解决方案吗?

【问题讨论】:

  • 不幸的是我无法让它工作。
  • 至少不会更好更快
  • 您不需要解析 \n 字符和存储索引。您已经拥有行集合 TextBoxBase.LinesTextBoxBase.GetFirstCharIndexFromLine 将返回特定行中每个起始字符的索引。仅在真正需要时使用提供的索引器:int CharIndex = richTextBox1.GetFirstCharIndexFromLine(100);
  • 我实际上已经做了一个这样的编辑器,我总是使用辅助函数来获取当前行号和该行的位置,通过使用rtb.SelectionStartrtb.GetLineFromCharIndex(...) 和@987654330 @。一旦你得到了,你就可以处理来自rtb.Lines[...]的单行。

标签: c# .net winforms richtextbox


【解决方案1】:

我使用富文本框和 40k 行尝试了您的示例和 Felix 的示例以及我自己的解决方案。结果是这是最快的,而且我没有看到减速。您可以尝试将行数组作为参数传递并告诉我们结果吗?

public static List<int> GetNewLineLocations(this string[] lines)
        {
            var list = new List<int>();
            int ix = -1;

            for (int i = 0; i < lines.Length; i++)
            {
                ix += lines[i].Length+1;
                list.Add(ix);
            }

            return list;
        }

【讨论】:

  • 无法并行求和行长,因为处理顺序混乱。你的结果是错误的。
  • 您应该过度考虑您的应用程序设计并将日期存储在您的用户界面中。 Check your Front/Backend seperation。将您的数据存储在后端将允许您直接访问您的数据,而无需依赖于您的 Textbox 方法。
  • @Felix - 你在哪里看到平行的东西?
  • 感谢您的帮助,参数是解决方案。
  • 不客气,我让比我聪明的人为什么在迭代器中使用对象引用要慢得多。
【解决方案2】:

使用字符串时,Regular Expressions 非常好用。但它们并不是最快的。如果您需要更快的处理,您应该在较低级别和并行进行。并确保使用 long 作为索引,因为 int 最多只能处理 2^31 个字符,最多可以处理 2^63 个字符。

我同意@Nyerguds 谁在 cmets 中说:

问题在于,在富文本框中获取文本的标准函数实际上是一个处理函数,必须过滤掉 RTF 标记。获取文本的实际功能是瓶颈,而不是之后的瓶颈。

因此,您的数据应该保存在代码中的某个位置,而不是用户界面中。处理长文本时迟早会引起麻烦,例如滚动时卡顿或进一步的瓶颈。而且我只会表示无论如何都可以在控件中显示的行。所以你应该过度考虑你的应用程序设计。 Check your Front/Backend seperation. 将您的数据存储在后端将允许您直接访问您的数据,而无需依赖于您的文本框方法或其他用户界面。

以下是如何使用 .net 框架的Parallel Class 轻松处理数据的示例:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Threading.Tasks;

    namespace ConsoleApp1
    {
        internal class Program
        {
            public static byte[] _globalDataStore { get; set; }
            private static void Main(string[] args)
            {
                DoStuff();
                ShowDone();
            }

            private static void ShowDone()
            {
                Console.WriteLine("done...");
                Console.ReadKey();
            }

            private static void DoStuff()
            {
                var tempData = GetData();
                StoreData(ref tempData);
                tempData = null; //free some ram
                var dataIdentifier = (byte)'\n';
                GetAndPromptDataPositions(_globalDataStore, dataIdentifier);
            }

            private static void GetAndPromptDataPositions<T>(T[] data, T dataIdentifier)
            {
                var dataPositionList = GetDataPositions<T>(data, dataIdentifier);
                PromptDataPostions(dataPositionList);
            }

            private static void PromptDataPostions(IEnumerable<long> positionList)
            {
                foreach (var position in positionList)
                {
                    Console.WriteLine($"Position '{position}'");
                }
            }
            private static string GetData()
            {
                return "aasdlj\naksdlkajsdlkasldj\nasld\njkalskdjasldjlasd";
            }

            private static void StoreData(ref string tempData)
            {
                _globalDataStore = Encoding.ASCII.GetBytes(tempData);
            }

            private static List<long> GetDataPositions<T>(T[] data, T dataToFind)
            {
                lock (data) //prevent data from being changed while processing, important when have other threaded could write data 
                {
                    var postitonList = new List<long>();
                    Parallel.For(0, data.LongLength, (position) =>
                    {
                        if (data[position].Equals(dataToFind))
                        {
                            lock (postitonList) //lock list because of multithreaded access to prevent data corruption
                            {
                                postitonList.Add(position);
                            }
                        }
                    });
                    return postitonList;
                }
            }
        }
    }

【讨论】:

  • 问题在于,在富文本框中获取文本的标准函数实际上是一个必须过滤掉 RTF 标记的处理函数。 获取文本的实际功能是瓶颈,而不是之后的瓶颈。
  • 你是对的。也想过类似的事情。在我看来,数据应该保存在代码中某处的字节数组中,而不是控件属性中。处理长文本时迟早会引起麻烦,例如滚动时口吃。我只会展示可以在控件中显示的行。
猜你喜欢
  • 1970-01-01
  • 2017-05-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多