用C#替换特殊字符的字符串

25

我的要求是:

我需要将一些特殊字符,如 * ' " , _ & # ^ @,替换为 string.Empty,并将空格替换为 -

这是我的代码:

 Charseparated = Charseparated
    .Replace("*","")
    .Replace("'","")
    .Replace("&","")
    .Replace("@","") ...

我需要替换这么多字符,所以必须使用同样数量的Replace,但我想避免这种做法。

有没有其他高效的方法可以删除特殊字符、同时将空格替换为-


使用正则表达式 - Myrtle
2
只是想补充一下,如果这是关于生成有效文件名的问题,您可以使用 Sytem.IO.Path.GetInvalidFileNameChars 获取无效字符集。 - Georg Patscheider
3
有何特殊之处?这些并不是特殊字符。你是想清理文件路径吗?读取带引号的CSV文本?净化SQL输入?针对每种情况都有更好的替代方案,不需要使用替换操作。 - Panagiotis Kanavos
StringBuilder.Replace() 是一个更高效的替代方案,用于替换字符串中的内容,如此处所讨论。但是您仍然需要使用多个 Replace() 调用。 - Seth Denburg
你有一个问题。你用正则表达式解决了它。现在你有两个问题。(抱歉,我忍不住要说这句玩笑话 :)) - Daniel Williams
14个回答

24

我认为,在这里最好使用以下正则表达式

s/[*'",_&#^@]/ /g
你可以使用 Regex 类来实现这个目的。
Regex reg = new Regex("[*'\",_&#^@]");
str1 = reg.Replace(str1, string.Empty);

Regex reg1 = new Regex("[ ]");
str1 = reg.Replace(str1, "-");

你的正则表达式在替换为string.empty和“-”时有什么区别?对我来说,似乎你总是用空格替换,这并不是要求中的内容。(或者我错了吗?) - Patrick Hofman
@PatrickHofman。好的,看到你在回答中进行了编辑,但是这样做,你的回答并没有展示出所有可能的字符,对吧? - Rahul
同意,但从概念上讲它是可行的。你的方案没有或者不够清晰。 - Patrick Hofman
2
@Venkat 已更正。 - Patrick Hofman
4
考虑一下静态方法Regex.Replace(input, pattern, replacement)。对于单次使用的正则表达式,我更喜欢使用它。 - DavidTheWin
显示剩余3条评论

10

使用正则表达式

Regex.Replace("Hello*Hello'Hello&Hello@Hello Hello", @"[^0-9A-Za-z ,]", "").Replace(" ", "-")

它将用string.Empty替换所有特殊字符,并用"-"替换空格。


这不是拼写错误 - 空格在那里。因为我们想用"-"替换空格。 - Mitesh Gadhiya
首先,它会用空字符串替换所有特殊字符。一开始使用正则表达式进行替换,会有空格,然后将空格替换为连字符。 - Mitesh Gadhiya
抱歉,我显然有点特别。你是完全正确的,我会删除我的评论(在给你足够时间阅读后)。我显然现在不应该看SO。;-) - Chris

8
Regex.Replace(source_string, @"[^\w\d]", "_");

该操作将把给定字符串(source_string)中所有非字母数字替换为“_”。


7
创建一个要进行更改的集合,并对其进行迭代:
var replacements = new []
                   { new { Old = "*", New = string.Empty }
                   // all your other replacements, removed for brevity
                   , new { Old = " ", New = "-" }
                   }

foreach (var r in replacements)
{
    Charseparated = Charseparated.Replace(r.Old, r.New);
}

这将会导致大量临时字符串的产生。如果有大量输入数据,将会造成巨大的内存浪费,并消耗大量CPU资源进行垃圾回收。 - Panagiotis Kanavos
@PanagiotisKanavos charseperated没有被声明,它可能是一个StringBuilder,如果不是的话,很可能应该是。 - Scrobi
@Corak,你的经验完全错误。这也取决于你测试了什么——一个一次性字符串有点牵强。这还取决于你测量了什么——你是否包括垃圾回收时间?如果你想得到可用的数字,请使用BenchmarkDotNet。当你看到真实的数字时,你会感到震惊的。 - Panagiotis Kanavos
@Corak 还可以尝试一个“真实”的例子。选择一个大的日志文件并尝试解析它。 - Panagiotis Kanavos
4
如果没有具体的使用情境,那么任何"性能测量"都是没有意义的。如果可能的输入大小平均低于20个字符,对10万个字符串进行测量是无关紧要的。在这种情况下,“避免过早优化”的意思是:让代码可工作和易维护,并且只有在测量结果表明必要时才进行优化。 - hoffmale
显示剩余9条评论

5
你可以尝试使用LINQ
  var source = "lala * lalala @ whowrotethis # ohcomeon &";

  var result = string.Concat(source.Select(c => c == ' ' 
     ? "-" 
     : "*'\",_&#^@".Contains(c) ? "" 
     : c.ToString()));

c.ToString() 每输入一个字符,都会创建一个临时字符串。最好只处理字符,然后在结尾将结果转换为字符串。 - Panagiotis Kanavos
一个 Where(c=>"*'\",_&#^@".Contains(c)).Select(c => c == ' ' ? '-':c) 也会导致 单次 扫描输入字符串 - where 消除不需要的字符,Select 进行替换。 - Panagiotis Kanavos
@PanagiotisKanavos:你的Where条件需要在前面加上一个!(因为它会返回所有特殊字符而不是其他所有内容)。 - hoffmale

4

使用 LINQ 和 char[] 的方法:

   string f = Filter("*WHAT/ #PO#PO");

它返回了什么?

    private string Filter(string s)
    {
        var chars = new[] { '*', '/', '#' };
        var filteredChars = s.ToArray();
        return new string(filteredChars
                 .Where(ch => !chars.Contains(ch) )
                 .Select(ch => ch == ' ' ? '-' : ch).ToArray());
    }

2

string.Replace非常糟糕,非常糟糕,非常糟糕,除了最微不足道的任务外,专业程序员不应在任何地方使用它。

字符串是不可变的。这意味着每次执行string.replace(或myString = myString +“lalala”等操作时),系统都需要进行所有后勤工作(创建新指针、复制内容、垃圾收集等)。顺便说一下,帕特里克上面的答案就是这样做的(但代码可读性更好)。

如果只需要执行几次,这不是问题--并且代码立即可读。

但是,一旦将此操作放入循环中,您需要以另一种方式编写它。我会自己去使用正则表达式:

string inStr = "lala * lalala @ whowrotethis # ohcomeon &";
string outStr = Regex.Replace(inStr , "[*|@|*|&]", string.Empty);

3
“string.Replace是可怕的,非常可怕,除了最琐碎的任务外,专业程序员不应在任何地方使用它。”这太可怕了。string.Replace必须小心使用,但不使用它并不能解决问题。 - Patrick Hofman
1
@PatrickHofman,由重复的Replace调用生成的多个临时字符串非常糟糕。在日志文件上尝试并观察内存使用量飙升到几GB。正则表达式可以以10倍的速度完成相同的工作,仅使用20MB,因为它不会生成无用的临时字符串。 - Panagiotis Kanavos
1
String.Replace 并不是“可怕、可怕、可怕”的,你是否应该使用它与你是否从事编程职业无关,而是取决于你想要实现什么。此外,你的正则表达式中有多余的 | 字符。 - Matti Virkkunen
2
@MattiVirkkunen .NET Core团队正在花费大量时间和精力消除虚假分配,甚至引入了新的UTF8String类。无论如何,多次替换都是不好的想法。这更多是关于规模-5次替换并不算太多。但如果有5000行,那就太多了。如果只需要过滤字符,则单个扫描和过滤字符比较好。 - Panagiotis Kanavos
@Matti “这篇文章一开始就说一个方法通常很糟糕”是的,这是有意为之的。我假设提问者不是资深同事。因此,我的帖子至少有50%的教学意图。告诉别人要避免什么,然后再给他们例外(如果他们过度使用,这是很常见的,那么它将处于“好但稍微不那么高效”的状态),比反过来更好。这通常被做到正是人们试图通过string.replace查看5GB日志文件的原因。 - Jim Andrakakis
显示剩余3条评论

2
这是最优和简单的方法。
    public void foo()
    {
        string input = "A sample input a*b#c@d";
        string unwanted = "*'\",_&#^@";
        List<char> unwantedChars = unwanted.ToList<char>();
        StringBuilder sb = new StringBuilder();

        input = input.Replace(' ', '-');
        foreach(char c in input)
        {
            if (!unwantedChars.Any(x => x == c))
                sb.Append(c);
        }
        string output = sb.ToString(); //A-sample-input-abcd
    }

转换为 List<char> 不是必要的。StringContains 方法,你可以直接在循环中使用:if (!unwanted.Contains(c)) - Mong Zhu
1
最优的吗?不是。最好的情况下,它将与正则表达式一样快。 - Panagiotis Kanavos

2

这位用户要求以“高效”的方式替换字符串。

就性能而言,使用正则表达式并不是最佳解决方案(但在可读性或方便性方面可能更好...)。

相反地,StringBuilder的性能更好,如果您处理大量数据,则这可能变得非常重要。

 StringBuilder sb = new StringBuilder(myString);
 foreach (string unwanted in collectionOfUnwantedStrings)
         {
             sb.Replace(unwanted, string.Empty);
         }

1
使用 string.Split 和分隔符字符数组,然后将它们聚合回一个字符串中。但是,用 string.Empty 替换和用 "-" 替换 " " 必须分别完成。
        var res = "23#$36^45&".Split(new[] {'#', '$', '^', '&'}, StringSplitOptions.RemoveEmptyEntries)
            .Aggregate((s, s1) => s + s1);
        // "233645"

1
.Aggregate is so unnatural here, why not string.Concat("23#$36^45&".Split(...)); - Dmitry Bychenko
没错...在这种情况下,string.Concat更适合,当你只想让那些字符消失时。但是我之前使用这种模式的场景也需要替换,聚合实际上类似于(s, s1) => s+ "_" + s1 - Stefan Balan

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