我正在编写自定义字符串拆分。它将分成一个点(.
),前面没有奇数个反斜杠(\
)。
«string» -> «IEnemerable<string>»
"hello.world" -> "hello", "world"
"abc\.123" -> "abc\.123"
"aoeui\\.dhtns" -> "aoeui\\","dhtns"
我想知道是否存在将重用原始字符串的子字符串(用于速度),或者是否存在可以快速执行此操作的现有拆分?
这就是我所拥有的,但是比input.Split('.')
//慢2-3倍,其中input是一个字符串。 (我知道这是一个(稍微复杂的问题,但不是那么多)
public IEnumerable<string> HandMadeSplit(string input)
{
var Result = new LinkedList<string>();
var word = new StringBuilder();
foreach (var ch in input)
{
if (ch == '.')
{
Result.AddLast(word.ToString());
word.Length = 0;
}
else
{
word.Append(ch);
}
}
Result.AddLast(word.ToString());
return Result;
}
它现在使用List而不是LinkedList,并记录子字符串的开头和结尾,并使用string.substring创建新的子字符串。这做了很多,几乎和string.split一样快,但我添加了我的调整。 (将添加代码)
答案 0 :(得分:3)
如果您需要表现,您展示的循环是正确的方法。 (正则表达式不会)。
切换到基于索引的for循环。记住比赛开始的索引。不要附加个人字符。相反,请记住要复制的字符范围,并通过每个项目Substring
调用来执行此操作。
另外,请勿使用LinkedList
。除了随机访问突变之外,几乎所有情况都比List
慢。
您也可以从List
切换到使用Array.Resize
调整大小的普通数组。这会导致稍微繁琐的代码(因为您已将List
类的一部分内联到您的方法中),但它会减少一些小的开销。
接下来,不要返回IEnumerable
,因为在访问其项目时会强制调用者通过间接方式。返回List
或数组。
答案 1 :(得分:3)
这是我最终确定的那个。它不像string.split那么快,但足够好并且可以修改,以做我想要的。
private IEnumerable<string> HandMadeSplit2b(string input)
{
//this one is margenaly better that the second best 2, but makes the resolver (its client much faster), nealy as fast as original.
var Result = new List<string>();
var begining = 0;
var len = input.Length;
for (var index=0;index<len;index++)
{
if (input[index] == '.')
{
Result.Add(input.Substring(begining,index-begining));
begining = index+1;
}
}
Result.Add(input.Substring(begining));
return Result;
}
答案 2 :(得分:2)
您不应该尝试使用string.Split
。
如果您需要帮助来实现它,解决此问题的一种简单方法是使用循环扫描字符串,跟踪找到符合条件的点的最后位置。当您找到新的限定点(或到达输入字符串的末尾)时,只需yield return
当前子字符串。
编辑:关于返回列表或数组与使用yield
如果在您的应用程序中,最重要的是调用者在迭代子字符串上花费的时间,那么您应该填充列表或数组并返回它,如接受的问题中所建议的那样。在收集子串时我不会使用可调整大小的数组,因为这会很慢。
另一方面,如果您关心整体性能和内存,并且有时调用者不必遍历整个列表,则应使用yield return
。当您使用yield return
时,您的优势在于,在调用者调用MoveNext
(直接或间接通过foreach
)之前,根本不会执行任何代码。这意味着您可以保存用于分配阵列/列表的内存,并节省分配/调整大小/填充列表所花费的时间。你将花费时间几乎只是在寻找子串的逻辑上,这将是懒惰的,即 - 只有当实际需要时,因为调用者继续迭代子串。