有没有更好的方法来替换字符串?
我很惊讶Replace方法不支持字符数组或者字符串数组。我猜我可以自己写一个扩展方法,但是我想知道是否有更好的内置方法来完成以下操作?注意最后一个Replace方法使用的是字符串而不是字符。
myString.Replace(';', '\n').Replace(',', '\n').Replace('\r', '\n').Replace('\t', '\n').Replace(' ', '\n').Replace("\n\n", "\n");
有没有更好的方法来替换字符串?
我很惊讶Replace方法不支持字符数组或者字符串数组。我猜我可以自己写一个扩展方法,但是我想知道是否有更好的内置方法来完成以下操作?注意最后一个Replace方法使用的是字符串而不是字符。
myString.Replace(';', '\n').Replace(',', '\n').Replace('\r', '\n').Replace('\t', '\n').Replace(' ', '\n').Replace("\n\n", "\n");
s/[;,\t\r ]|[\n]{2}/\n/g
s/
开头表示搜索[
和]
之间的字符是要搜索的字符(任意顺序)/
分隔搜索文本和替换文本用英语表达如下:
"搜索;
、,
、\t
、\r
、空格或正好两个连续的\n
,并将其替换为\n
"
在C#中,您可以执行以下操作:(导入System.Text.RegularExpressions
后)
Regex pattern = new Regex("[;,\t\r ]|[\n]{2}");
pattern.Replace(myString, "\n");
\t
和\r
被包含在\s
中。因此,你的正则表达式等同于[;,\s]
。 - NullUserExceptionReplace("\n\n", "\n")
,而您的正则表达式无法处理该操作。 - NullUserExceptionchar[] separators = new char[]{' ',';',',','\r','\t','\n'};
string s = "this;is,\ra\t\n\n\ntest";
string[] temp = s.Split(separators, StringSplitOptions.RemoveEmptyEntries);
s = String.Join("\n", temp);
public static class ExtensionMethods
{
public static string Replace(this string s, char[] separators, string newVal)
{
string[] temp;
temp = s.Split(separators, StringSplitOptions.RemoveEmptyEntries);
return String.Join( newVal, temp );
}
}
然后,就完成了...
char[] separators = new char[]{' ',';',',','\r','\t','\n'};
string s = "this;is,\ra\t\n\n\ntest";
s = s.Replace(separators, "\n");
Regex.Replace
比多次连续使用 string.Replace
更慢 8 倍,并且比 Split
+ Join
慢 4 倍。请参见:https://gist.github.com/MarcinJuraszek/c1437d925548561ba210a1c6ed144452。 - MarcinJuraszekstring s = "the\nquick\tbrown\rdog,jumped;over the lazy fox.";
char[] chars = new char[] { ' ', ';', ',', '\r', '\t', '\n' };
string snew = chars.Aggregate(s, (c1, c2) => c1.Replace(c2, '\n'));
这里是扩展方法:
public static string ReplaceAll(this string seed, char[] chars, char replacementCharacter)
{
return chars.Aggregate(seed, (str, cItem) => str.Replace(cItem, replacementCharacter));
}
扩展方法使用示例:
string snew = s.ReplaceAll(chars, '\n');
myString = Regex.Replace(myString, @"[;,\t\r ]|[\n]{2}", "\n");
哦,这场表演真是太可怕了!答案略有过时,但仍然...
public static class StringUtils
{
#region Private members
[ThreadStatic]
private static StringBuilder m_ReplaceSB;
private static StringBuilder GetReplaceSB(int capacity)
{
var result = m_ReplaceSB;
if (null == result)
{
result = new StringBuilder(capacity);
m_ReplaceSB = result;
}
else
{
result.Clear();
result.EnsureCapacity(capacity);
}
return result;
}
public static string ReplaceAny(this string s, char replaceWith, params char[] chars)
{
if (null == chars)
return s;
if (null == s)
return null;
StringBuilder sb = null;
for (int i = 0, count = s.Length; i < count; i++)
{
var temp = s[i];
var replace = false;
for (int j = 0, cc = chars.Length; j < cc; j++)
if (temp == chars[j])
{
if (null == sb)
{
sb = GetReplaceSB(count);
if (i > 0)
sb.Append(s, 0, i);
}
replace = true;
break;
}
if (replace)
sb.Append(replaceWith);
else
if (null != sb)
sb.Append(temp);
}
return null == sb ? s : sb.ToString();
}
}
你只需要让它可变:
StringBuilder
unsafe
的世界并使用指针进行操作 (虽然很危险)并尽量减少字符数组迭代的次数。注意这里使用了 HashSet
,因为它避免了在循环内部遍历字符序列。如果需要更快的查找,可以使用基于 array[256]
的优化查找来替换 HashSet
。
使用 StringBuilder 的示例
public static void MultiReplace(this StringBuilder builder,
char[] toReplace,
char replacement)
{
HashSet<char> set = new HashSet<char>(toReplace);
for (int i = 0; i < builder.Length; ++i)
{
var currentCharacter = builder[i];
if (set.Contains(currentCharacter))
{
builder[i] = replacement;
}
}
}
编辑 - 优化版本(仅适用于ASCII)
public static void MultiReplace(this StringBuilder builder,
char[] toReplace,
char replacement)
{
var set = new bool[256];
foreach (var charToReplace in toReplace)
{
set[charToReplace] = true;
}
for (int i = 0; i < builder.Length; ++i)
{
var currentCharacter = builder[i];
if (set[currentCharacter])
{
builder[i] = replacement;
}
}
}
那么你只需要像这样使用它:
var builder = new StringBuilder("my bad,url&slugs");
builder.MultiReplace(new []{' ', '&', ','}, '-');
var result = builder.ToString();
wchar_t
类型的。您只替换了所有可能字符的子集(并且您需要使用65536个布尔值来优化...) - gogpublic static string ReplaceChars(this string s, char[] separators, char newVal)
{
StringBuilder sb = new StringBuilder(s);
foreach (var c in separators) { sb.Replace(c, newVal); }
return sb.ToString();
}
foreach (char c in separators) { s = s.Replace(c, '\n'); }
我个人认为这是最好的选择。它非常高效,不需要编写扩展方法。在我的测试中,它仅用了63ms就运行了100,000次迭代,使其成为最有效的选项。 以下是一个示例:
string s = "this;is,\ra\t\n\n\ntest";
char[] separators = new char[] { ' ', ';', ',', '\r', '\t', '\n' };
foreach (char c in separators) { s = s.Replace(c, '\n'); }
感谢Paul Walls提供本示例的前两行。
您还可以直接编写这些字符串扩展方法,然后将它们放在您的解决方案中的任何位置:
using System.Text;
public static class StringExtensions
{
public static string ReplaceAll(this string original, string toBeReplaced, string newValue)
{
if (string.IsNullOrEmpty(original) || string.IsNullOrEmpty(toBeReplaced)) return original;
if (newValue == null) newValue = string.Empty;
StringBuilder sb = new StringBuilder();
foreach (char ch in original)
{
if (toBeReplaced.IndexOf(ch) < 0) sb.Append(ch);
else sb.Append(newValue);
}
return sb.ToString();
}
public static string ReplaceAll(this string original, string[] toBeReplaced, string newValue)
{
if (string.IsNullOrEmpty(original) || toBeReplaced == null || toBeReplaced.Length <= 0) return original;
if (newValue == null) newValue = string.Empty;
foreach (string str in toBeReplaced)
if (!string.IsNullOrEmpty(str))
original = original.Replace(str, newValue);
return original;
}
}
"ABCDE".ReplaceAll("ACE", "xy");
xyBxyDxy
And this:
"ABCDEF".ReplaceAll(new string[] { "AB", "DE", "EF" }, "xy");
xyCxyF
Span
类型和string.Create
方法。oldChar ^ newChar
(XOR)值,这样可以获得以下好处:
ch ^ ch = 0
- 不需要初始化非更改项ch ^ repl[ch]
:
ch ^ 0 = ch
- 没有更改的字符情况ch ^ (ch ^ newChar) = newChar
- 替换的字符ArrayPool<char>
来避免每次调用ReplaceAll
方法时进行分配。为了确保数组被清零而不需要昂贵的调用Array.Clear
方法,我们将维护一个专门用于ReplaceAll
方法的池。我们将在将替换数组(仅确切的项目)返回到池之前清除它。public static class StringExtensions
{
private static readonly ArrayPool<char> _replacementPool = ArrayPool<char>.Create();
public static string ReplaceAll(this string str, char newChar, params char[] oldChars)
{
// If nothing to do, return the original string.
if (string.IsNullOrEmpty(str) ||
oldChars is null ||
oldChars.Length == 0)
{
return str;
}
// If only one character needs to be replaced,
// use the more efficient `string.Replace`.
if (oldChars.Length == 1)
{
return str.Replace(oldChars[0], newChar);
}
// Get a replacement array from the pool.
var replacements = _replacementPool.Rent(char.MaxValue + 1);
try
{
// Intialize the replacement array in the way that
// all elements represent `oldChar ^ newChar`.
foreach (var oldCh in oldChars)
{
replacements[oldCh] = (char)(newChar ^ oldCh);
}
// Create a string with replaced characters.
return string.Create(str.Length, (str, replacements), (dst, args) =>
{
var repl = args.replacements;
foreach (var ch in args.str)
{
dst[0] = (char)(repl[ch] ^ ch);
dst = dst.Slice(1);
}
});
}
finally
{
// Clear the replacement array.
foreach (var oldCh in oldChars)
{
replacements[oldCh] = char.MinValue;
}
// Return the replacement array back to the pool.
_replacementPool.Return(replacements);
}
}
}
string input = "This is text with far too much " +
"whitespace.";
string pattern = "[;,]";
string replacement = "\n";
Regex rgx = new Regex(pattern);
string result = rgx.Replace(input, replacement);
这里有更多关于 RegEx.Replace 的 MSDN 文档。