从C#字符串中删除字符

193

我该如何从字符串中删除字符?比如:"My name @is ,Wan.;'; Wan"

我想要从该字符串中删除字符'@',',','.',';','\'',使其变为"My name is Wan Wan"


这个问题的真正正确解决方法在微软文档中已经简单地解释了:https://learn.microsoft.com/en-us/dotnet/standard/base-types/how-to-strip-invalid-characters-from-a-string。再也没有比这更容易的了。 - Fattie
22个回答

228
var str = "My name @is ,Wan.;'; Wan";
var charsToRemove = new string[] { "@", ",", ".", ";", "'" };
foreach (var c in charsToRemove)
{
    str = str.Replace(c, string.Empty);
}

但是如果您想删除所有非字母字符,我可以建议另一种方法

var str = "My name @is ,Wan.;'; Wan";
str = new string((from c in str
                  where char.IsWhiteSpace(c) || char.IsLetterOrDigit(c)
                  select c
       ).ToArray());

13
可以这样做,str = new string(str.Where(x=>char.IsWhiteSpace(x)||char.IsLetterOrDigit(x)).ToArray());(该代码行为C#语言) - Adnan Bhatti
1
我不得不查一下,string.Empty不会为比较创建一个字符串,因此它比""更有效。(来源: https://dev59.com/XXVC5IYBdhLWcg3w51hv) - Tom Cerul
7
只有我一个人在使用 string.Empty 时遇到了“参数2:无法将'string'转换为'char'”的错误吗? - OddDev
2
@OddDev,如果您循环遍历的数组是字符列表,则应该会出现此错误。如果它们是字符串,那么这应该可以工作。 - Newteq Developer
6
请注意,如果您想将 string.Empty 作为第二个参数使用,则 str.Replace 函数的第一个参数必须是“string”。如果您使用 char(例如'a')作为第一个参数,则第二个参数也需要是 char。否则,您将会收到 @OddDev 上面提到的“参数2:无法从'string'转换为'char'”错误。 - Leo
回应@Leo的评论,但以不同的方式: "char"变量可能被命名为某些带有"str"的东西,因为这些是字符串,而不仅仅是单个'字符'。 - NovaDev

91

简单:

String.Join("", "My name @is ,Wan.;'; Wan".Split('@', ',' ,'.' ,';', '\''));

3
虽然可读性不是很好,但它似乎是这里最高效的解决方案。请参见评论 - MonkeyDreamzzz
1
或者将空字符串上的 Join 替换为 Concatstring.Concat("My name @is ,Wan.;'; Wan".Split('@', ',' ,'.' ,';', '\'')); - zcoop98

73

听起来这似乎是一个正则表达式的理想应用,因为它是专门设计用于快速文本操作的引擎。在这种情况下:

Regex.Replace("He\"ll,o Wo'r.ld", "[@,\\.\";'\\\\]", string.Empty)

3
如果您可以使用已编译的正则表达式,那么这种方法似乎比基于迭代器的方法更有效。 - Ade Miller
这应该是被接受的答案,特别是因为像@AdeMiller所说的那样,它会更加高效。 - mattyb
25
这并不比循环快,一般认为正则表达式总是比循环快是一个误解。正则表达式并不神奇,本质上必须在某个点迭代字符串才能执行它们的操作,并且它们可能因为正则表达式本身的开销而变得更慢。当需要进行非常复杂的操作,需要几十行代码和多重循环时,它们真正出色。测试编译后的正则表达式与简单未优化的循环相比,执行50000次,正则表达式慢了6倍。 - Tony Cheetham
内存效率如何?正则表达式在新字符串分配方面不会更有效吗? - Marek
2
也许我在断言正则表达式很快时说错了。除非它是非常紧密的循环中心,否则对于像这样的小操作,可读性和可维护性很可能会主导性能。 - John Melville

45

比较不同建议(以及在目标具有不同大小和位置的单字符替换上下文中进行比较)。

在这种特定情况下,将字符串拆分为目标并在替换(在此情况下为空字符串)上进行连接是最快的,至少快了3倍。最终,性能取决于替换的数量,在源中替换的位置以及源的大小。#ymmv

结果

(完整结果请点击此处

| Test                      | Compare | Elapsed                                                            |
|---------------------------|---------|--------------------------------------------------------------------|
| SplitJoin                 | 1.00x   | 29023 ticks elapsed (2.9023 ms) [in 10K reps, 0.00029023 ms per]   |
| Replace                   | 2.77x   | 80295 ticks elapsed (8.0295 ms) [in 10K reps, 0.00080295 ms per]   |
| RegexCompiled             | 5.27x   | 152869 ticks elapsed (15.2869 ms) [in 10K reps, 0.00152869 ms per] |
| LinqSplit                 | 5.43x   | 157580 ticks elapsed (15.758 ms) [in 10K reps, 0.0015758 ms per]   |
| Regex, Uncompiled         | 5.85x   | 169667 ticks elapsed (16.9667 ms) [in 10K reps, 0.00169667 ms per] |
| Regex                     | 6.81x   | 197551 ticks elapsed (19.7551 ms) [in 10K reps, 0.00197551 ms per] |
| RegexCompiled Insensitive | 7.33x   | 212789 ticks elapsed (21.2789 ms) [in 10K reps, 0.00212789 ms per] |
| Regex Insensitive         | 7.52x   | 218164 ticks elapsed (21.8164 ms) [in 10K reps, 0.00218164 ms per] |

测试工具 (LinqPad)

(注意: PerfVs 是我编写的计时扩展)

void test(string title, string sample, string target, string replacement) {
    var targets = target.ToCharArray();
    
    var tox = "[" + target + "]";
    var x = new Regex(tox);
    var xc = new Regex(tox, RegexOptions.Compiled);
    var xci = new Regex(tox, RegexOptions.Compiled | RegexOptions.IgnoreCase);

    // no, don't dump the results
    var p = new Perf/*<string>*/();
        p.Add(string.Join(" ", title, "Replace"), n => targets.Aggregate(sample, (res, curr) => res.Replace(new string(curr, 1), replacement)));
        p.Add(string.Join(" ", title, "SplitJoin"), n => String.Join(replacement, sample.Split(targets)));
        p.Add(string.Join(" ", title, "LinqSplit"), n => String.Concat(sample.Select(c => targets.Contains(c) ? replacement : new string(c, 1))));
        p.Add(string.Join(" ", title, "Regex"), n => Regex.Replace(sample, tox, replacement));
        p.Add(string.Join(" ", title, "Regex Insentive"), n => Regex.Replace(sample, tox, replacement, RegexOptions.IgnoreCase));
        p.Add(string.Join(" ", title, "Regex, Uncompiled"), n => x.Replace(sample, replacement));
        p.Add(string.Join(" ", title, "RegexCompiled"), n => xc.Replace(sample, replacement));
        p.Add(string.Join(" ", title, "RegexCompiled Insensitive"), n => xci.Replace(sample, replacement));
    
    var trunc = 40;
    var header = sample.Length > trunc ? sample.Substring(0, trunc) + "..." : sample;
    
    p.Vs(header);
}

void Main()
{
    // also see https://dev59.com/sms05IYBdhLWcg3wJ-uQ
    
    "Control".Perf(n => { var s = "*"; });
    
        
    var text = "My name @is ,Wan.;'; Wan";
    var clean = new[] { '@', ',', '.', ';', '\'' };
    
    test("stackoverflow", text, string.Concat(clean), string.Empty);

    
    var target = "o";
    var f = "x";
    var replacement = "1";
    
    var fillers = new Dictionary<string, string> {
        { "short", new String(f[0], 10) },
        { "med", new String(f[0], 300) },
        { "long", new String(f[0], 1000) },
        { "huge", new String(f[0], 10000) }
    };
    
    var formats = new Dictionary<string, string> {
        { "start", "{0}{1}{1}" },
        { "middle", "{1}{0}{1}" },
        { "end", "{1}{1}{0}" }
    };

    foreach(var filler in fillers)
    foreach(var format in formats) {
        var title = string.Join("-", filler.Key, format.Key);
        var sample = string.Format(format.Value, target, filler.Value);
        
        test(title, sample, target, replacement);
    }
}

25

针对您的问题不是那么具体,但可以通过在正则表达式中列出可接受的字符来将一个字符串中的所有标点符号(除空格外)全部删除:

string dirty = "My name @is ,Wan.;'; Wan";

// only space, capital A-Z, lowercase a-z, and digits 0-9 are allowed in the string
string clean = Regex.Replace(dirty, "[^A-Za-z0-9 ]", "");

请注意在数字9后面有一个空格,以免从您的句子中删除空格。第三个参数是一个空字符串,用于替换不属于正则表达式的任何子字符串。


19
 string x = "My name @is ,Wan.;'; Wan";
 string modifiedString = x.Replace("@", "").Replace(",", "").Replace(".", "").Replace(";", "").Replace("'", "");

这样做不起作用,因为string.Replace返回一个“修改后的字符串”。请参见https://dev59.com/XGYr5IYBdhLWcg3w7udx#13277669。 - Esteban Verbel

11

最简单的方法是使用String.Replace函数:

String s = string.Replace("StringToReplace", "NewString");

8

这是我编写的一种方法,它采用了略微不同的方法。与指定要删除的字符不同,我告诉我的方法我想保留哪些字符 - 它将删除所有其他字符。

在OP的示例中,他只想保留字母字符和空格。以下是调用我的方法的示例 (C#演示):

var str = "My name @is ,Wan.;'; Wan";

// "My name is Wan Wan"
var result = RemoveExcept(str, alphas: true, spaces: true);

这是我的方法:

/// <summary>
/// Returns a copy of the original string containing only the set of whitelisted characters.
/// </summary>
/// <param name="value">The string that will be copied and scrubbed.</param>
/// <param name="alphas">If true, all alphabetical characters (a-zA-Z) will be preserved; otherwise, they will be removed.</param>
/// <param name="numerics">If true, all numeric characters (0-9) will be preserved; otherwise, they will be removed.</param>
/// <param name="dashes">If true, all dash characters (-) will be preserved; otherwise, they will be removed.</param>
/// <param name="underlines">If true, all underscore characters (_) will be preserved; otherwise, they will be removed.</param>
/// <param name="spaces">If true, all whitespace (e.g. spaces, tabs) will be preserved; otherwise, they will be removed.</param>
/// <param name="periods">If true, all dot characters (".") will be preserved; otherwise, they will be removed.</param>
public static string RemoveExcept(string value, bool alphas = false, bool numerics = false, bool dashes = false, bool underlines = false, bool spaces = false, bool periods = false) {
    if (string.IsNullOrWhiteSpace(value)) return value;
    if (new[] { alphas, numerics, dashes, underlines, spaces, periods }.All(x => x == false)) return value;

    var whitelistChars = new HashSet<char>(string.Concat(
        alphas ? "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" : "",
        numerics ? "0123456789" : "",
        dashes ? "-" : "",
        underlines ? "_" : "",
        periods ? "." : "",
        spaces ? " " : ""
    ).ToCharArray());

    var scrubbedValue = value.Aggregate(new StringBuilder(), (sb, @char) => {
        if (whitelistChars.Contains(@char)) sb.Append(@char);
        return sb;
    }).ToString();

    return scrubbedValue;
}

太棒了的回答! - edtheprogrammerguy
非常好!数字字符串中两次出现了0。 - John Kurtz
@JohnKurtz 很好的发现 - 现在已经消失了。 - Mass Dot Net
这似乎是搞砸包含特殊字符或变音符号的文本的好方法... - Nyerguds

7
从@drzaus获取性能数据,以下是使用最快算法的扩展方法。
public static class StringEx
{
    public static string RemoveCharacters(this string s, params char[] unwantedCharacters) 
        => s == null ? null : string.Join(string.Empty, s.Split(unwantedCharacters));
}

使用方法

var name = "edward woodward!";
var removeDs = name.RemoveCharacters('d', '!');
Assert.Equal("ewar woowar", removeDs); // old joke

6
另一个简单的解决方案:
var forbiddenChars = @"@,.;'".ToCharArray();
var dirty = "My name @is ,Wan.;'; Wan";
var clean = new string(dirty.Where(c => !forbiddenChars.Contains(c)).ToArray());

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接