在richtextbox中查找所有换行符

3

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

我需要一个函数,可以获取文本中每个换行符“\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;
    }

第一个解决方案确实可以工作,但输入到richTextBox中的文本越多,它就会变得越慢。大约40000个字符可以分散在很多行中,例如20000行。
第二个方案似乎更快,因为它循环较少并且大致相同,但无论这些行包含多少文本,它都会急剧减速到1000行左右。
当然,代码需要运行得快,并且不使用太多资源,这就是我认为第二个解决方案更好的原因。
我的问题是:
1.哪种解决方案更好?为什么?
2.为什么第二个解决方案如此缓慢?
3.有没有更好的解决方案?

很遗憾,我无法让它正常工作。 - HDP11
至少不更好也不更快 - HDP11
2
您无需解析\n字符并存储索引。您已经拥有行的集合TextBoxBase.Lines,而TextBoxBase.GetFirstCharIndexFromLine将返回特定行中每个起始字符的索引。仅在真正需要时使用提供的索引器:int CharIndex = richTextBox1.GetFirstCharIndexFromLine(100); - Jimi
确保您查看秒表,以测量处理时间并比较算法。 - Felix Quehl
1
我实际上已经制作了一个类似的编辑器,并且我总是使用一个辅助函数来获取当前行号和该行上的位置,方法是使用 rtb.SelectionStartrtb.GetLineFromCharIndex(...)rtb.GetFirstCharIndexFromLine(...)。一旦你得到了这个,你就可以处理 rtb.Lines[...] 中的单行内容。 - Nyerguds
2个回答

1

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

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;
        }

并行计算行长度的总和是不可能的,因为处理顺序是混乱的。你得到的结果会是错误的。 - Felix Quehl
你应该重新考虑你的应用程序设计,不要将数据存储在用户界面中。检查你的前端/后端分离。将数据存储在后端将允许你直接访问数据,而不依赖于文本框方法。 - Felix Quehl
@Felix - 你在哪里看到有并行处理的地方了? - Kevin
谢谢您的帮助,参数是解决方案。 - HDP11
不客气,我让比我聪明的人解释为什么在迭代器中使用对象引用会慢得多。 - Kevin

0

在处理字符串时,正则表达式 是非常好用的。但它们并不是最快的。如果你需要更快的处理速度,应该在更低的层次和并行处理。并且要确保使用 long 作为索引,因为 int 只允许你处理最多 2^31 个字符,而 long 可以处理最多 2^63 个字符。

我同意 @Nyerguds 在评论中所说的:

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

因此,您的数据应该存储在代码中而不是用户界面中。早晚处理长文本时,这将导致问题,例如滚动时卡顿或其他瓶颈。我只会表示可以在控件中显示的行。因此,您应该重新考虑应用程序设计。检查前/后端分离。将数据存储在后端中将允许您直接访问数据,而不依赖于您的Textbox方法或其他用户界面内容。

以下是如何使用.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;
                }
            }
        }
    }

1
问题在于获取富文本框中文本的标准函数实际上是一个处理函数,必须过滤掉RTF标记。_获取文本的实际函数_是瓶颈,而不是其后续操作。 - Nyerguds
你说得对。我也考虑过类似的事情。在我看来,数据应该在代码中的某个字节数组中保存,而不是在控件属性中保存。早晚处理长文本时,这样做会导致问题,比如滚动时卡顿。我只会呈现可以在控件中显示的行。 - Felix Quehl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接