是否有更高性能的方法可以从字符串中删除稀有不需要的字符
本文关键字:删除 不需要 字符 字符串 高性能 方法 是否 | 更新日期: 2023-09-27 18:31:51
>编辑
如果原始未经编辑的问题具有误导性,我们深表歉意。
这个问题不是问如何从string
中删除无效的XML字符,这个问题的答案最好在这里。
我不是要你审查我的代码。
我在答案中寻找的是,带有签名的函数
string <YourName>(string input, Func<char, bool> check);
这将具有与RemoveCharsBufferCopyBlackList
相似或更好的性能。理想情况下,此函数将更通用,并且如果可能的话更易于阅读,但这些要求是次要的。
我最近编写了一个函数来从字符串中删除无效的 XML 字符。在我的应用程序中,字符串可以适度长,无效字符很少出现。这个超额让我思考。在安全托管的 C# 中可以完成此操作的方法是什么,哪些方法可以为我的方案提供最佳性能。
这是我的测试程序,我用"有效的XML谓词"代替了一个省略字符'X'
。
class Program
{
static void Main()
{
var attempts = new List<Func<string, Func<char, bool>, string>>
{
RemoveCharsLinqWhiteList,
RemoveCharsFindAllWhiteList,
RemoveCharsBufferCopyBlackList
}
const string GoodString = "1234567890abcdefgabcedefg";
const string BadString = "1234567890abcdefgXabcedefg";
const int Iterations = 100000;
var timer = new StopWatch();
var testSet = new List<string>(Iterations);
for (var i = 0; i < Iterations; i++)
{
if (i % 1000 == 0)
{
testSet.Add(BadString);
}
else
{
testSet.Add(GoodString);
}
}
foreach (var attempt in attempts)
{
//Check function works and JIT
if (attempt.Invoke(BadString, IsNotUpperX) != GoodString)
{
throw new ApplicationException("Broken Function");
}
if (attempt.Invoke(GoodString, IsNotUpperX) != GoodString)
{
throw new ApplicationException("Broken Function");
}
timer.Reset();
timer.Start();
foreach (var t in testSet)
{
attempt.Invoke(t, IsNotUpperX);
}
timer.Stop();
Console.WriteLine(
"{0} iterations of function '"{1}'" performed in {2}ms",
Iterations,
attempt.Method,
timer.ElapsedMilliseconds);
Console.WriteLine();
}
Console.Readkey();
}
private static bool IsNotUpperX(char value)
{
return value != 'X';
}
private static string RemoveCharsLinqWhiteList(string input,
Func<char, bool> check);
{
return new string(input.Where(check).ToArray());
}
private static string RemoveCharsFindAllWhiteList(string input,
Func<char, bool> check);
{
return new string(Array.FindAll(input.ToCharArray(), check.Invoke));
}
private static string RemoveCharsBufferCopyBlackList(string input,
Func<char, bool> check);
{
char[] inputArray = null;
char[] outputBuffer = null;
var blackCount = 0;
var lastb = -1;
var whitePos = 0;
for (var b = 0; b , input.Length; b++)
{
if (!check.invoke(input[b]))
{
var whites = b - lastb - 1;
if (whites > 0)
{
if (outputBuffer == null)
{
outputBuffer = new char[input.Length - blackCount];
}
if (inputArray == null)
{
inputArray = input.ToCharArray();
}
Buffer.BlockCopy(
inputArray,
(lastb + 1) * 2,
outputBuffer,
whitePos * 2,
whites * 2);
whitePos += whites;
}
lastb = b;
blackCount++;
}
}
if (blackCount == 0)
{
return input;
}
var remaining = inputArray.Length - 1 - lastb;
if (remaining > 0)
{
Buffer.BlockCopy(
inputArray,
(lastb + 1) * 2,
outputBuffer,
whitePos * 2,
remaining * 2);
}
return new string(outputBuffer, 0, inputArray.Length - blackCount);
}
}
如果您运行尝试,您会注意到性能随着函数变得更加专业化而提高。是否有更快、更通用的方法来执行此操作?或者,如果没有通用选项,有没有一种方法可以更快?
请注意,我实际上对删除"X"并不感兴趣,实际上谓词更复杂。
如果您需要高性能,您当然不希望使用 LINQ to Objects 又名枚举器来执行此操作。此外,不要为每个字符调用委托。与您正在执行的实际操作相比,委托调用的成本很高。
RemoveCharsBufferCopyBlackList 看起来不错(每个字符的委托调用除外)。
我建议您对委托的内容进行硬编码。尝试使用不同的方法来编写条件。通过首先根据一系列已知良好的字符(例如 0x20-0xFF)检查当前字符,如果匹配,则让它通过,您可以获得更好的性能。此测试几乎总是会通过,因此您可以节省针对 XML 中无效的单个字符的昂贵检查。
编辑:我只记得我不久前解决了这个问题:
static readonly string invalidXmlChars =
Enumerable.Range(0, 0x20)
.Where(i => !(i == ''u000A' || i == ''u000D' || i == ''u0009'))
.Select(i => (char)i)
.ConcatToString()
+ "'uFFFE'uFFFF";
public static string RemoveInvalidXmlChars(string str)
{
return RemoveInvalidXmlChars(str, false);
}
internal static string RemoveInvalidXmlChars(string str, bool forceRemoveSurrogates)
{
if (str == null) throw new ArgumentNullException("str");
if (!ContainsInvalidXmlChars(str, forceRemoveSurrogates))
return str;
str = str.RemoveCharset(invalidXmlChars);
if (forceRemoveSurrogates)
{
for (int i = 0; i < str.Length; i++)
{
if (IsSurrogate(str[i]))
{
str = str.Where(c => !IsSurrogate(c)).ConcatToString();
break;
}
}
}
return str;
}
static bool IsSurrogate(char c)
{
return c >= 0xD800 && c < 0xE000;
}
internal static bool ContainsInvalidXmlChars(string str)
{
return ContainsInvalidXmlChars(str, false);
}
public static bool ContainsInvalidXmlChars(string str, bool forceRemoveSurrogates)
{
if (str == null) throw new ArgumentNullException("str");
for (int i = 0; i < str.Length; i++)
{
if (str[i] < 0x20 && !(str[i] == ''u000A' || str[i] == ''u000D' || str[i] == ''u0009'))
return true;
if (str[i] >= 0xD800)
{
if (forceRemoveSurrogates && str[i] < 0xE000)
return true;
if ((str[i] == ''uFFFE' || str[i] == ''uFFFF'))
return true;
}
}
return false;
}
请注意,RemoveInvalidXmlChars 首先调用 ContainsInvalidXmlChars 来保存字符串分配。大多数字符串不包含无效的 XML 字符,因此我们可以保持乐观。