[英]Replace multiple characters in a C# string
有沒有更好的方法來替換字符串?
我很驚訝 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");
如果您感覺特別聰明並且不想使用 Regex:
char[] 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);
您也可以毫不費力地將其包裝在擴展方法中。
編輯:或者等 2 分鍾,我還是會寫完的 :)
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");
您可以使用 Linq 的 Aggregate 函數:
string 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
,因為它避免遍歷循環內的字符序列。 如果您需要更快的查找,您可以用優化的char
查找替換HashSet
(基於array[256]
)。
使用 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;
}
}
}
編輯 - 優化版本
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();
您也可以簡單地編寫這些字符串擴展方法,並將它們放在您的解決方案中的某個位置:
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
還有這個:
"ABCDEF".ReplaceAll(new string[] { "AB", "DE", "EF" }, "xy");
xyCxyF
性能方面,這可能不是最好的解決方案,但它有效。
var str = "filename:with&bad$separators.txt";
char[] charArray = new char[] { '#', '%', '&', '{', '}', '\\', '<', '>', '*', '?', '/', ' ', '$', '!', '\'', '"', ':', '@' };
foreach (var singleChar in charArray)
{
str = str.Replace(singleChar, '_');
}
string ToBeReplaceCharacters = @"~()@#$%&+,'"<>|;\/*?";
string fileName = "filename;with<bad:separators?";
foreach (var RepChar in ToBeReplaceCharacters)
{
fileName = fileName.Replace(RepChar.ToString(), "");
}
使用 RegEx.Replace,如下所示:
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 文檔的更多信息
用於將一組定義的字符串字符替換為特定字符的 .NET Core 版本。 它利用了最近引入的Span
類型和string.Create
方法。
這個想法是准備一個替換數組,所以每個字符串字符不需要實際的比較操作。 因此,替換過程提醒了狀態機的工作方式。 為了避免替換數組的所有項目的初始化,讓我們在那里存儲oldChar ^ newChar
(XOR'ed) 值,這樣做有以下好處:
ch ^ ch = 0
- 不需要初始化不變的項目ch ^ repl[ch]
:
ch ^ 0 = ch
- 不改變字符大小寫ch ^ (ch ^ newChar) = newChar
- 替換的字符所以唯一的要求是確保替換數組在初始化時為零。 每次調用ReplaceAll
方法時,我們將使用ArrayPool<char>
來避免分配。 並且,為了確保數組被清零而無需昂貴地調用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);
}
}
}
我知道這個問題非常古老,但我想提供兩個更有效的選項:
首先,Paul Walls 發布的擴展方法很好,但可以通過使用 StringBuilder 類來提高效率,該類類似於字符串數據類型,但特別適用於您將多次更改字符串值的情況。 這是我使用 StringBuilder 制作的擴展方法的一個版本:
public 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();
}
我運行了 100,000 次此操作,使用 StringBuilder 需要 73 毫秒,而使用字符串需要 81 毫秒。 因此,差異通常可以忽略不計,除非您正在運行許多操作或使用巨大的字符串。
其次,這是您可以使用的 1 個襯里循環:
foreach (char c in separators) { s = s.Replace(c, '\n'); }
我個人認為這是最好的選擇。 它非常高效,不需要編寫擴展方法。 在我的測試中,這僅在 63 毫秒內運行了 100k 次迭代,使其成為最高效的。 這是上下文中的示例:
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'); }
本示例中的前 2 行歸功於 Paul Walls。
沒有“替換”(僅限 Linq):
string myString = ";,\r\t \n\n=1;;2,,3\r\r4\t\t5 6\n\n\n\n7=";
char NoRepeat = '\n';
string ByeBye = ";,\r\t ";
string myResult = myString.ToCharArray().Where(t => !"STOP-OUTSIDER".Contains(t))
.Select(t => "" + ( ByeBye.Contains(t) ? '\n' : t))
.Aggregate((all, next) => (
next == "" + NoRepeat && all.Substring(all.Length - 1) == "" + NoRepeat
? all : all + next ) );
我也擺弄過這個問題,發現這里的大多數解決方案都很慢。 最快的實際上是dodgy_coder發布的LINQ + Aggregate 方法。
但我想,根據有多少舊字符,內存分配可能也很重。 所以我想出了這個:
這里的想法是為當前線程緩存舊字符的替換映射,以安全分配。 除此之外,只是使用輸入的字符數組,稍后再次作為字符串返回。 而字符數組被修改的越少越好。
[ThreadStatic]
private static bool[] replaceMap;
public static string Replace(this string input, char[] oldChars, char newChar)
{
if (input == null) throw new ArgumentNullException(nameof(input));
if (oldChars == null) throw new ArgumentNullException(nameof(oldChars));
if (oldChars.Length == 1) return input.Replace(oldChars[0], newChar);
if (oldChars.Length == 0) return input;
replaceMap = replaceMap ?? new bool[char.MaxValue + 1];
foreach (var oldChar in oldChars)
{
replaceMap[oldChar] = true;
}
try
{
var count = input.Length;
var output = input.ToCharArray();
for (var i = 0; i < count; i++)
{
if (replaceMap[input[i]])
{
output[i] = newChar;
}
}
return new string(output);
}
finally
{
foreach (var oldChar in oldChars)
{
replaceMap[oldChar] = false;
}
}
}
對我來說,這最多是兩個用於實際輸入字符串的分配。 由於某些原因, StringBuilder
對我來說要慢得多。 它比 LINQ 變體快 2 倍。
在構建了我自己的解決方案並查看了此處使用的解決方案之后,我利用了一個不使用復雜代碼並且通常對大多數參數都有效的答案。
Append
等其他 StringBuilder 方法所做的那樣)。params
關鍵字輕松傳遞額外的字符串。 但是,如果您喜歡其他順序,則不必執行此操作。namespace Test.Extensions
{
public static class StringExtensions
{
public static string ReplaceAll(this string str, char replacementCharacter, params char[] chars)
{
if (chars.Length == 0)
return str;
if (chars.Length == 1)
return str.Replace(chars[0], replacementCharacter);
StringBuilder sb = new StringBuilder(str.Length);
var searcher = new HybridDictionary(chars.Length);
for (int i = 0; i < chars.Length; i++)
searcher[chars[i]] = true;
foreach (var c in str)
{
if (searcher.Contains(c))
sb.Append(replacementCharacter);
else
sb.Append(c);
}
return sb.ToString();
}
public static StringBuilder ReplaceAll(this StringBuilder sb, char replacementCharacter, params char[] chars)
{
if (chars.Length == 0)
return sb;
if (chars.Length == 1)
return sb.Replace(chars[0], replacementCharacter);
var searcher = new HybridDictionary(chars.Length);
for (int i = 0; i < chars.Length; i++)
searcher[chars[i]] = true;
for (int i = 0; i < sb.Length; i++)
{
var val = sb[i];
if (searcher.Contains(val))
sb[i] = replacementCharacter;
}
return sb;
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.