有许多类似的问题,但显然没有完美的匹配,这就是我提问的原因。我想通过一组字符串分隔符(例如
更新:我进行了一些性能测试并比较了结果(虽然太懒了以至于没有正式检查它们)。测试过的解决方案如下(随机顺序):Gabe,Guffa,Mafu,Regex。其他解决方案未经测试,因为它们要么与另一个解决方案相似,要么来得太晚。
xx
, yy
)拆分一个随机字符串(例如123xx456yy789
),并将分隔符包含在结果中(这里是123
,xx
,456
,yy
,789
)。良好的性能是一个不错的奖励。如果可能的话,应避免使用正则表达式。更新:我进行了一些性能测试并比较了结果(虽然太懒了以至于没有正式检查它们)。测试过的解决方案如下(随机顺序):Gabe,Guffa,Mafu,Regex。其他解决方案未经测试,因为它们要么与另一个解决方案相似,要么来得太晚。
这是测试代码:
class Program
{
private static readonly List<Func<string, List<string>, List<string>>> Functions;
private static readonly List<string> Sources;
private static readonly List<List<string>> Delimiters;
static Program ()
{
Functions = new List<Func<string, List<string>, List<string>>> ();
Functions.Add ((s, l) => s.SplitIncludeDelimiters_Gabe (l).ToList ());
Functions.Add ((s, l) => s.SplitIncludeDelimiters_Guffa (l).ToList ());
Functions.Add ((s, l) => s.SplitIncludeDelimiters_Naive (l).ToList ());
Functions.Add ((s, l) => s.SplitIncludeDelimiters_Regex (l).ToList ());
Sources = new List<string> ();
Sources.Add ("");
Sources.Add (Guid.NewGuid ().ToString ());
string str = "";
for (int outer = 0; outer < 10; outer++) {
for (int i = 0; i < 10; i++) {
str += i + "**" + DateTime.UtcNow.Ticks;
}
str += "-";
}
Sources.Add (str);
Delimiters = new List<List<string>> ();
Delimiters.Add (new List<string> () { });
Delimiters.Add (new List<string> () { "-" });
Delimiters.Add (new List<string> () { "**" });
Delimiters.Add (new List<string> () { "-", "**" });
}
private class Result
{
public readonly int FuncID;
public readonly int SrcID;
public readonly int DelimID;
public readonly long Milliseconds;
public readonly List<string> Output;
public Result (int funcID, int srcID, int delimID, long milliseconds, List<string> output)
{
FuncID = funcID;
SrcID = srcID;
DelimID = delimID;
Milliseconds = milliseconds;
Output = output;
}
public void Print ()
{
Console.WriteLine ("S " + SrcID + "\tD " + DelimID + "\tF " + FuncID + "\t" + Milliseconds + "ms");
Console.WriteLine (Output.Count + "\t" + string.Join (" ", Output.Take (10).Select (x => x.Length < 15 ? x : x.Substring (0, 15) + "...").ToArray ()));
}
}
static void Main (string[] args)
{
var results = new List<Result> ();
for (int srcID = 0; srcID < 3; srcID++) {
for (int delimID = 0; delimID < 4; delimID++) {
for (int funcId = 3; funcId >= 0; funcId--) { // i tried various orders in my tests
Stopwatch sw = new Stopwatch ();
sw.Start ();
var func = Functions[funcId];
var src = Sources[srcID];
var del = Delimiters[delimID];
for (int i = 0; i < 10000; i++) {
func (src, del);
}
var list = func (src, del);
sw.Stop ();
var res = new Result (funcId, srcID, delimID, sw.ElapsedMilliseconds, list);
results.Add (res);
res.Print ();
}
}
}
}
}
正如您所看到的,这只是一个快速而肮脏的测试,但我运行了多次测试,并以不同的顺序进行了测试,结果始终非常一致。测量时间范围为毫秒级到较大数据集的几秒钟。由于在实践中它们似乎微不足道,我在接下来的评估中忽略了低毫秒范围内的值。以下是我的输出结果:
S 0 D 0 F 3 11毫秒 1 S 0 D 0 F 2 7毫秒 1 S 0 D 0 F 1 6毫秒 1 S 0 D 0 F 0 4毫秒 0 S 0 D 1 F 3 28毫秒 1 S 0 D 1 F 2 8毫秒 1 S 0 D 1 F 1 7毫秒 1 S 0 D 1 F 0 3毫秒 0 S 0 D 2 F 3 30毫秒 1 S 0 D 2 F 2 8毫秒 1 S 0 D 2 F 1 6毫秒 1 S 0 D 2 F 0 3毫秒 0 S 0 D 3 F 3 30毫秒 1 S 0 D 3 F 2 10毫秒 1 S 0 D 3 F 1 8毫秒 1 S 0 D 3 F 0 3毫秒 0 S 1 D 0 F 3 9毫秒 1 9e5282ec-e2a2-4... S 1 D 0 F 2 6毫秒 1 9e5282ec-e2a2-4... S 1 D 0 F 1 5毫秒 1 9e5282ec-e2a2-4... S 1 D 0 F 0 5毫秒 1 9e5282ec-e2a2-4... S 1 D 1 F 3 63毫秒 9 9e5282ec - e2a2 - 4265 - 8276 - 6dbb50fdae37 S 1 D 1 F 2 37毫秒 9 9e5282ec - e2a2 - 4265 - 8276 - 6dbb50fdae37 S 1 D 1 F 1 29毫秒 9 9e5282ec - e2a2 - 4265 - 8276 - 6dbb50fdae37 S 1 D 1 F 0 22毫秒 9 9e5282ec - e2a2 - 4265 - 8276 - 6dbb50fdae37 S 1 D 2 F 3 30毫秒 1 9e5282ec-e2a2-4... S 1 D 2 F 2 10毫秒 1 9e5282ec-e2a2-4... S 1 D 2 F 1 10毫秒 1 9e5282ec-e2a2-4... S 1 D 2 F 0 12毫秒 1 9e5282ec-e2a2-4... S 1 D 3 F 3 73毫秒 9 9e5282ec - e2a2 - 4265 - 8276 - 6dbb50fdae37 S 1 D 3 F 2 40毫秒 9 9e5282ec - e2a2 - 4265 - 8276 - 6dbb50fdae37 S 1 D 3 F 1 33毫秒 9 9e5282ec - e2a2 - 4265 - 8276 - 6dbb50fdae37 S 1 D 3 F 0 30毫秒 9 9e5282ec - e2a2 - 4265 - 8276 - 6dbb50fdae37 S 2 D 0 F 3 10毫秒 1 0**我比较了结果,发现如下:
- 所有4个函数对于一般用途来说都足够快。
- 朴素版本(即最初的版本)在计算时间方面最差。
- 正则表达式在小数据集上稍微慢一点(可能是初始化开销导致的)。
- 正则表达式在大数据上表现良好,并且与非正则表达式的解决方案速度相当。
- 从性能角度来看,总体上最好的是Guffa的版本,这符合代码预期。
- Gabe的版本有时会省略一个项目,但我没有调查这个问题(bug?)。
总之,我建议使用正则表达式,它相当快。如果性能很重要,则更喜欢Guffa的实现。
pattern = "(" + String.Join("|", (from d in delimeters select Regex.Escape(d)).ToArray()) + ")"
,因为任何一个分隔符中都可能包含有.
或|
或其它字符。 - GabeSelect
之后还需要一个ToArray()
,将IEnumerable<string>
转换为string[]
以供String.Join
使用。 - Ahmad Mageedpattern = @"\(\)";
或pattern = Regex.Escape("()");
来获得相同的结果,以避免任何奇怪的情况发生。Regex.Escape将转义:\, *, +, ?, |, {, [, (,), ^, $,., #
。如果delimiters
列表为空,则最终的模式构建将不正确地变成()
, 因此需要检查以避免在空列表上进行拆分:if (delimiters.Count > 0) { // build pattern and then split, otherwise do nothing }
。这个检查通常是很有必要的。 - Ahmad Mageed