Bir dizenin 10 karakterden birini içerip içermediğini kontrol edin


108

C # kullanıyorum ve bir dizenin on karakterden birini, *, &, # vb. İçerip içermediğini kontrol etmek istiyorum.

En iyi yol nedir?


1
Karakterlerden herhangi birinin orada olup olmadığını veya bu karakterlerden "birini" (yani: Tam olarak birini) ve yalnızca birini içerip içermediğini mi görmek istiyorsunuz?
Reed Copsey

Yanıtlar:


211

Bana göre en basit yöntem şu:

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

Veya okuması muhtemelen daha kolay bir biçimde:

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

Gereken bağlama ve performansa bağlı olarak, char dizisini önbelleğe almak isteyebilir veya istemeyebilirsiniz.


Char dizisi somutlaştırılırken tür atlanabilir ve çıkarılır.
Palec

41

Başkalarının da söylediği gibi IndexOfAny kullanın. Ancak bunu şu şekilde kullanırım:

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

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

Bu şekilde, her aramada yeni bir dizi oluşturmazsınız. Dizenin taranması, bir dizi karakter değişmezi olan IMO'dan daha kolaydır.

Elbette, bunu yalnızca bir kez kullanacaksanız, boşa harcanan yaratım bir sorun teşkil etmez, ya şunu kullanabilirsiniz:

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

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

veya

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

Hangisini daha okunaklı bulduğunuza, noktalama karakterlerini başka bir yerde kullanmak isteyip istemediğinize ve yöntemin ne sıklıkla çağrılacağına bağlıdır.


DÜZENLEME: Aşağıda, bir dizenin karakterlerden tam olarak birini içerip içermediğini bulmak için Reed Copsey'in yöntemine bir alternatif var .

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

Sanırım performans bir sorunsa char dizisini önbelleğe almaya değer, ancak yine de bağlama bağlı olarak buna değmeyebilir.
Noldorin

1
Evet, eğer onu sadece bir kez uygulanacak bir yöntemde kullanıyorsanız, buna değmeyebilir. Ancak, performansı kadar okunabilirliği de arttırdığını düşünüyorum. ToCharArrayElbette gerekirse "satır içi" formunu kullanabilirsiniz .
Jon Skeet

1
@canon: Set ne kadar büyük? Çok çok küçük kümeler için Array.Contains'in daha hızlı olmasını beklerdim. Büyük kümeler için, HashSet muhtemelen kilometrelerce kazanır.
Jon Skeet

5

Sadece herhangi bir karakter içerip içermediğini görmek istiyorsanız, başka bir yerde önerildiği gibi string.IndexOfAny kullanmanızı tavsiye ederim.

Bir dizenin tam olarak on karakterden birini ve yalnızca birini içerdiğini doğrulamak istiyorsanız , o zaman biraz daha karmaşık hale gelir. İnanıyorum ki en hızlı yol bir Kavşak ile karşılaştırmak ve sonra tekrarları kontrol etmek.

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

Evet, sanırım bu durumda tek bir döngü muhtemelen daha hızlıdır, özellikle de küçük noktalama işaretleriyle. Hangisinin gerçekten daha hızlı olduğunu görmek için bunu büyük dizelerle test etmeyi merak ediyorum.
Reed Copsey

1
Sanırım iki dizginin kesişimini bulmanın her halükarda karakter karakter gitmesi gerekecek, bu yüzden nasıl daha hızlı olacağını göremiyorum ... ve önerdiğim rotam sadece tek bir geçiş kullanmakla kalmıyor, aynı zamanda "erken çıkış" seçeneği. Metnin bir milyon karakter uzunluğunda olduğunu, ancak ilk ikisinin her ikisi de "*" olduğunu düşünün :)
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

Herkese teşekkürler! (Ve esas olarak Jon!): Bu, bunu yazmama izin verdi:

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

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

çünkü belirli bir dizenin gerçekten bir fiyat mı yoksa bir cümle mi olduğunu anlamak için iyi bir yol arıyordum, örneğin 'Gösterilemeyecek kadar düşük'.


2
Bunun eski olduğunu biliyorum, ancak açık olmak gerekirse bu, para birimlerini eşleştirmenin özellikle iyi bir yolu değil ... Eğer birisi "Ke $ ha" yazmış olsaydı, bu bir fiyat olarak eşleşirdi ... Bunun yerine, burada tanımlanan para birimini algıla: stackoverflow.com/questions/7214513/…
mcse3010
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.