检查一个字符串是否包含10个字符中的任意一个

129
我正在使用C#,想要检查一个字符串是否包含十个字符之一,如*、&、#等等。
最好的方法是什么?

1
你想查看是否有任何字符存在,或者它是否包含那些字符中的“一个”(即:仅一个)? - Reed Copsey
6个回答

248

以下是我认为最简单的方法:

var match = str.IndexOfAny(new char[] { '*', '&', '#' }) != -1

或者以一种可能更易于阅读的形式:

var match = str.IndexOfAny("*&#".ToCharArray()) != -1

根据上下文和所需的性能,您可能需要或不需要缓存字符数组。


在实例化字符数组时,类型可以省略,它将被推断。 - Palec

47

如其他人所说,使用IndexOfAny。然而,我会这样使用:

private static readonly char[] Punctuation = "*&#...".ToCharArray();

public static bool ContainsPunctuation(string text)
{
    return text.IndexOfAny(Punctuation) >= 0;
}

这样,您就不会在每次调用时创建一个新数组。在我看来,与一系列字符字面量相比,字符串的扫描也更容易。

当然,如果您只打算使用一次,那么浪费的创建并不是问题,您可以使用:

private const string Punctuation = "*&#...";

public static bool ContainsPunctuation(string text)
{
    return text.IndexOfAny(Punctuation.ToCharArray()) >= 0;
}
或者
public static bool ContainsPunctuation(string text)
{
    return text.IndexOfAny("*&#...".ToCharArray()) >= 0;
}

这真的取决于哪个更易读,你是否想在其他地方使用标点符号以及该方法将被调用的频率。


编辑:这是一种替代 Reed Copsey 方法的方法,用于查找字符串中是否包含 正好一个 字符。

private static readonly HashSet<char> Punctuation = new HashSet<char>("*&#...");

public static bool ContainsOnePunctuationMark(string text)
{
    bool seenOne = false;

    foreach (char c in text)
    {
        // TODO: Experiment to see whether HashSet is really faster than
        // Array.Contains. If all the punctuation is ASCII, there are other
        // alternatives...
        if (Punctuation.Contains(c))
        {
            if (seenOne)
            {
                return false; // This is the second punctuation character
            }
            seenOne = true;
        }
    }
    return seenOne;
}

如果性能是问题,我想缓存字符数组是值得的,但是根据情况而定,这可能并不值得。 - Noldorin
1
是的,如果你只在一个只会执行一次的方法中使用它,那么可能不值得。但是,我认为它可以提高可读性和性能。当然,如果需要的话,你可以使用“inline”的 ToCharArray 形式。 - Jon Skeet
1
@canon:这个集合有多大?对于非常小的集合,我期望Array.Contains会更快。对于大型集合,HashSet很可能会轻松胜出。 - Jon Skeet

6

5

如果您只想查看字符串是否包含任何字符,我建议使用string.IndexOfAny,如其他地方所建议的。

如果您想验证一个字符串是否恰好包含十个字符中的一个,并且仅有一个,则会变得更加复杂。我认为最快的方法是检查交集,然后检查重复项。

private static char[] characters = new char [] { '*','&',... };

public static bool ContainsOneCharacter(string text)
{
    var intersection = text.Intersect(characters).ToList();
    if( intersection.Count != 1)
        return false; // Make sure there is only one character in the text

    // Get a count of all of the one found character
    if (1 == text.Count(t => t == intersection[0]) )
        return true;

    return false;
}

是的 - 我想在这种情况下,单个循环可能更快,特别是对于少量标点符号。我很想尝试使用大字符串测试这一点,看看哪个真正更快。 - Reed Copsey
1
我认为找到两个字符串的交集必须逐个字符进行,所以我看不出它会更快...而我的建议路线不仅使用单次遍历,而且还具有“提前退出”的选项。想象一下,如果文本长度为一百万个字符,但前两个字符都是“*” :) - Jon Skeet

1
var specialChars = new[] {'\\', '/', ':', '*', '<', '>', '|', '#', '{', '}', '%', '~', '&'};

foreach (var specialChar in specialChars.Where(str.Contains))
{
    Console.Write(string.Format("string must not contain {0}", specialChar));
}

0
感谢大家!(尤其是Jon!)这让我能够写出这个:
    private static readonly char[] Punctuation = "$€£".ToCharArray();

    public static bool IsPrice(this string text)
    {
        return text.IndexOfAny(Punctuation) >= 0;
    }

当我正在寻找一种好的方法来检测某个字符串是价格还是句子,比如“太低以至于不显示”时。


2
我知道这已经过时了,但是要明确的是,这不是一种特别好的匹配货币的方法... 如果有人写了“Ke$ha”,它会被匹配为价格...相反,请参考在此定义货币的一种正确的检测方式: https://dev59.com/-1rUa4cB1Zd3GeqPmbES - mcse3010

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