编辑2:
确认我的性能问题是由于对StringExtensions类的静态函数调用造成的。一旦删除,IndexOf方法确实是实现此目的的最快方法。
查看字符串是否包含C#中的另一个字符串的最快,不区分大小写的方法是什么?我在Case insensitive 'Contains(string)'看到了这个帖子的公认解决方案,但是我已经做了一些初步的基准测试,看起来使用该方法会导致对大字符串(> 100个字符)的调用速度降低几次,只要测试字符串不能被发现。
以下是我所知道的方法:
的IndexOf:
public static bool Contains(this string source, string toCheck, StringComparison comp)
{
if (string.IsNullOrEmpty(toCheck) || string.IsNullOrEmpty(source))
return false;
return source.IndexOf(toCheck, comp) >= 0;
}
ToUpper的:
source.ToUpper().Contains(toCheck.ToUpper());
正则表达式:
bool contains = Regex.Match("StRiNG to search", "string", RegexOptions.IgnoreCase).Success;
所以我的问题是,这实际上是平均最快的方式,为什么呢?
编辑:
这是我用来突出性能差异的简单测试应用程序。使用它,我看到ToLower()为16 ms,ToUpper为18 ms,StringExtensions.Contains()为140 ms:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
namespace ScratchConsole
{
class Program
{
static void Main(string[] args)
{
string input = "";
while (input != "exit")
{
RunTest();
input = Console.ReadLine();
}
}
static void RunTest()
{
List<string> s = new List<string>();
string containsString = "1";
bool found;
DateTime now;
for (int i = 0; i < 50000; i++)
{
s.Add("AAAAAAAAAAAAAAAA AAAAAAAAAAAA");
}
now = DateTime.Now;
foreach (string st in s)
{
found = st.ToLower().Contains(containsString);
}
Console.WriteLine("ToLower(): " + (DateTime.Now - now).TotalMilliseconds);
now = DateTime.Now;
foreach (string st in s)
{
found = st.ToUpper().Contains(containsString);
}
Console.WriteLine("ToUpper(): " + (DateTime.Now - now).TotalMilliseconds);
now = DateTime.Now;
foreach (string st in s)
{
found = StringExtensions.Contains(st, containsString, StringComparison.OrdinalIgnoreCase);
}
Console.WriteLine("StringExtensions.Contains(): " + (DateTime.Now - now).TotalMilliseconds);
}
}
public static class StringExtensions
{
public static bool Contains(this string source, string toCheck, StringComparison comp)
{
return source.IndexOf(toCheck, comp) >= 0;
}
}
}
答案 0 :(得分:15)
由于ToUpper实际上会导致创建一个新字符串,因此StringComparison.OrdinalIgnoreCase会更快,同样,正则表达式对于像这样的简单比较有很多开销。也就是说,String.IndexOf(String,StringComparison.OrdinalIgnoreCase)应该是最快的,因为它不涉及创建新的字符串。
我猜想(我又去了)RegEx有更好的最坏情况因为它如何评估字符串,IndexOf将始终进行线性搜索,我猜测(并再次)RegEx正在使用一些东西更好。 RegEx也应该有一个最好的案例,可能会比IndexOf更接近(尽管不是很好)(由于它的语言更加复杂)。
15,000 length string, 10,000 loop
00:00:00.0156251 IndexOf-OrdinalIgnoreCase
00:00:00.1093757 RegEx-IgnoreCase
00:00:00.9531311 IndexOf-ToUpper
00:00:00.9531311 IndexOf-ToLower
Placement in the string also makes a huge difference:
At start:
00:00:00.6250040 Match
00:00:00.0156251 IndexOf
00:00:00.9687562 ToUpper
00:00:01.0000064 ToLower
At End:
00:00:00.5781287 Match
00:00:01.0468817 IndexOf
00:00:01.4062590 ToUpper
00:00:01.4218841 ToLower
Not Found:
00:00:00.5625036 Match
00:00:01.0000064 IndexOf
00:00:01.3750088 ToUpper
00:00:01.3906339 ToLower
答案 1 :(得分:1)
我发现编译后的RegEx是目前最快的解决方案,显然更加通用。编译它有助于使其与较小的字符串比较相提并论,如您所述,没有与较大的字符串进行比较。
http://www.dijksterhuis.org/regular-expressions-advanced/包含一些提示,以便从RegEx比较中获得最大速度;你会发现它很有帮助。
答案 2 :(得分:0)
这对我来说很有趣,所以我用不同的方法创建了一个小测试
string content = "";
for (var i = 0; i < 10000; i++)
content = String.Format("{0} asldkfjalskdfjlaskdfjalskdfj laksdf lkwiuirh 9238 r9849r8 49834", content);
string test = String.Format("{0} find_me {0}", content);
string search = test;
var tickStart = DateTime.Now.Ticks;
//6ms
//var b = search.ToUpper().Contains("find_me".ToUpper());
//2ms
//Match m = Regex.Match(search, "find_me", RegexOptions.IgnoreCase);
//a little bit over 1ms
var c = false;
if (search.Length == search.ToUpper().Replace("find_me".ToUpper(), "x").Length)
c = true;
var tickEnd = DateTime.Now.Ticks;
Debug.Write(String.Format("{0} {1}", tickStart, tickEnd));
所以我所做的就是创建一个字符串并在其中搜索
第一种方法search.ToUpper().Contains("find_me".ToUpper())
5ms
第二种方法Match m = Regex.Match(search, "find_me", RegexOptions.IgnoreCase)
2ms
第三种方法
if (search.Length == search.ToUpper().Replace("find_me".ToUpper(), "x").Length)
c = true;
花了不到1毫秒