Перевірте, чи містить рядок один з 10 символів


107

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

Який найкращий спосіб?


1
Ви хочете дізнатися, чи є там якийсь із персонажів, чи він містить "один" (тобто саме такий) цих символів, і лише один?
Рід Копсей

Відповіді:


210

На мій погляд, найпростішим методом був би наступний:

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

Або в можливо легшій для читання формі:

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

Залежно від необхідного контексту та продуктивності, ви можете або не захочете кешувати масив символів.


При екземплярі масиву char тип може бути опущений і буде зроблено висновок.
Palec

40

Як уже говорили інші, використовуйте 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;
}

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


EDIT: Ось альтернатива методу Ріда Копсі для з'ясування того, чи містить рядок точно один із символів.

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, якщо продуктивність є проблемою, але потім, можливо, це не варто того, залежно від контексту.
Нолдорін

1
Так, якщо ви використовуєте його лише у методі, який буде виконуватися раз, можливо, це не варто. Однак я думаю, що це покращує читабельність, а також продуктивність. Ви ToCharArray, звичайно, можете скористатися формою "inline".
Джон Скіт

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

тому що я шукав хороший спосіб визначити, чи певна рядок насправді є ціною чи пропозицією, як-от "Занадто низька кількість для показу".


2
Я знаю, що це по-старому, але для того, щоб зрозуміти, це не особливо вдалий спосіб співставити валюти ... Якби у вас хтось написав "Ke $ ha", це відповідало б ціні ... Натомість зверніться до одного правильного способу виявлення валюти , визначену тут: stackoverflow.com/questions/7214513 / ...
mcse3010
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.