Проверьте, содержит ли строка один из 10 символов

108

Я использую C # и хочу проверить, содержит ли строка один из десяти символов, *, &, # и т. Д.

Как лучше всего?

Джейд М
источник
1
Вы хотите увидеть, есть ли там какой-либо из символов или он содержит «один» (то есть: ровно один) из этих символов и только один?
Рид Копси,

Ответы:

211

На мой взгляд, самый простой способ:

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

Или в более удобной для чтения форме:

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

В зависимости от требуемого контекста и производительности вы можете или не захотите кэшировать массив char.

Нолдорин
источник
При создании экземпляра массива char тип может быть опущен, и он будет выведен.
Palec
41

Как говорили другие, используйте IndexOfAny. Однако я бы использовал его так:

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

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

Таким образом, вы не создадите новый массив при каждом вызове. Строку также легче сканировать, чем серию символьных литералов, IMO.

Конечно, если вы собираетесь использовать это только один раз, чтобы потраченное впустую создание не было проблемой, вы можете использовать:

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;
}

Это действительно зависит от того, что вы считаете более читаемым, хотите ли вы использовать символы пунктуации в другом месте и как часто будет вызываться метод.


РЕДАКТИРОВАТЬ: Вот альтернатива методу Рида Копси для определения того, содержит ли строка ровно один из символов.

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;
}
Джон Скит
источник
Я полагаю, что стоит кэшировать массив char, если производительность является проблемой, но опять же, это может не стоить того, в зависимости от контекста.
Noldorin
1
Да, если вы используете его только в методе, который будет выполняться один раз, оно того не стоит. Однако я думаю, что это улучшает читаемость, а также производительность. ToCharArrayКонечно, при необходимости вы можете использовать встроенную форму.
Джон Скит,
1
@canon: Насколько велик набор? Для очень и очень маленьких наборов я бы ожидал, что Array.Contains будет быстрее. Для больших наборов HashSet может выиграть много миль.
Джон Скит
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;
}
Рид Копси
источник
Да, я полагаю, что в этом случае одиночный цикл, вероятно, будет быстрее, особенно с небольшим набором знаков препинания. Мне было бы любопытно попробовать протестировать это с большими строками, чтобы увидеть, что действительно быстрее.
Рид Копси,
1
Я думаю, что поиск пересечения двух строк в любом случае придется выполнять символ за символом, поэтому я не вижу, как это будет быстрее ... и мой предлагаемый маршрут не только использует один проход, но также имеет вариант «досрочного выхода». Представьте, что текст состоит из миллиона символов, но первые два содержат «*» :)
Джон Скит,
1
var specialChars = new[] {'\\', '/', ':', '*', '<', '>', '|', '#', '{', '}', '%', '~', '&'};

foreach (var specialChar in specialChars.Where(str.Contains))
{
    Console.Write(string.Format("string must not contain {0}", specialChar));
}
нолого
источник
0

Всем спасибо! (И в основном Джон!): Это позволило мне написать следующее:

    private static readonly char[] Punctuation = "$€£".ToCharArray();

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

поскольку я искал хороший способ определить, была ли определенная строка на самом деле ценой или предложением, например «Слишком мало для отображения».

BernardG
источник
2
Я знаю, что это старый, но чтобы прояснить, это не особенно хороший способ сопоставления валют ... Если бы кто-то написал "Ke $ ha", это соответствовало бы цене ... Вместо этого обратитесь к одному правильному способу определить валюту,
указанную