2009-04-23 9 views
3

J'essaie de trouver le moyen le plus simple de rechercher un string pour un tableau de string s. Je sais que la façon la plus simple de faire cela pour les personnages est d'utiliser myString.IndexOfAny(charArray). Mais comment faire si je voudrais rechercher mon string pour string s et pas seulement les caractères? Y a-t-il des astuces .net ou des méthodes qui facilitent cela?Un moyen facile de rechercher une chaîne pour les chaînes

En fait, je voudrais faire quelque chose comme ceci:

string myName = "rahkim"; 
string[] names = new string[] {"joe","bob","chris"}; 

if(myName.IndexOfAny(names) >= 0) 
{ 
     //success code// 
} 

Je sais qu'il ya des façons de le faire avec des boucles, etc. Mais j'espérais quelque chose d'inhérent dans le cadre.

+1

Je suis en désaccord avec le double. Il semble vouloir rechercher une sous-chaîne dans chaque chaîne du tableau. – Samuel

+0

@Samuel, d'où obtenez-vous cela, il a clairement myName et recherche à travers un tableau de "noms"? – TStamper

+1

@TStamper: De IndexOfAny (...), à moins qu'il ne soit terriblement confus au sujet des méthodes de chaîne C#. – Samuel

Répondre

3

Vous pouvez (aussi) utilisez le staticIndexOf m éthode de la classe Array:

bool hasName = Array.IndexOf(names, myName) > -1; 
+0

Cela semblait être le meilleur ajustement. Merci. – rahkim

2

est ici la bonne syntaxe:

if(names.Contains(myName)) 
{ 
     //success code// 
} 
+0

Je pensais que l'OP voulait faire des correspondances de sous-chaînes .. ou n'est-ce pas? – Gishu

+0

Je ne reçois pas 'Contient' en option. Je reçois seulement des méthodes de tableau quand l'intellisense apparaît. – rahkim

+0

Inclure Linq pour obtenir l'extension - voir la question en double – daveb

1
if (names.Contains(myName)) 
{ 
//success code// 
} 
10

Vous devez définir si vous souhaitez trouver des chaînes égales ou rechercher un sous-chaîne correspondante. Les deux façons sont faciles pré-LINQ et avec LINQ.

string myName = "rahkim"; 
string[] names = new string[] { "joe", "bob", "chris" }; 
cordes égales, LINQ
bool contains = names.Contains(myName); 
Cordes Equal, Pré-LINQ
bool contains = new List<string>(name).Contains(myName); 
sous-chaînes, LINQ
bool contains = names.Any(name => name.Contains(myName)); 
Substring, pré-LINQ
bool contains = false; 
foreach(string name in names) 
    if (name.Contains(myName)) 
    contains = true; 
+0

Merci pour ces exemples. J'ai vraiment aimé la chaîne "Equal Strings, Pre-LINQ", mais elle m'a donné une erreur - Argument '1': impossible de convertir 'string []' en 'string'. Im probablement manquer quelque chose ici – rahkim

+0

C'est parce qu'il devrait être: 'bool contains = new Liste (noms) .Contains (myName);' BTW, sous-chaînes, LINQ peut également être plus court: 'bool contains = noms .Any (myName.Contains); ' – NetMage

3

int IndexOfAny (String [] RGS) serait en effet agréable mais il est théoriquement une opération O (n^2). Si, dans votre application, l'ensemble des chaînes rgs est grand et toujours le même, l'approche la plus efficace consiste à les charger une fois dans une structure de données trie, puis à les utiliser à plusieurs reprises pour les rechercher dans les chaînes inconnues lors de l'exécution.

Voici le code pertinent, adapté d'une source C# trie trouvée sur le web, attribuée à "Kerry D. Wong". Dans ma version, chaque chaîne dans le trie a une "charge utile" de type générique TValue. Pour utiliser cette fonction pour rechercher simplement les sous-chaînes, la charge utile peut toujours être définie sur true, comme illustré avec simple_trie.

L'autre chose que j'ai changé ici est que ce trie adapte automatiquement le stockage des chaînes Unicode arbitraires. Le tableau de chaque nœud, qui caractérise un trie, ajuste sa base et sa longueur pour s'adapter à la plage de caractères Unicode devant être stockée sur ce nœud. Cela permet une correspondance sensible à la casse, par exemple. La syntaxe d'initialisation C# 3.0 est pratique pour cette opération, mais son activation nécessite une implémentation fictive de IEnumerable afin de compiler. Le CLR ne semble pas appeler GetEnumerator() et je suggère que vous n'essayez pas d'énumérer avec son résultat non plus.

using System; 
using System.Collections.Generic; 
using System.Linq; // only used in Main() 

class Program 
{ 
    // trie with payload of type <String> 
    static Trie<String> value_trie = new Trie<String> 
    { 
     { "rabbit", "cute" }, 
     { "giraffe", "tall" }, 
     { "ape", "smart" }, 
     { "hippo", "large" }, 
    }; 

    // degenerate case of a trie without payload 
    static Trie<bool> simple_trie = new Trie<bool> 
    { 
     { "rabbit", true }, 
     { "giraffe", true }, 
     { "ape", true }, 
     { "hippo", true }, 
    }; 

    static void Main(String[] args) 
    { 
     String s = "Once upon a time, a rabbit met an ape in the woods."; 

     // Retrieve payloads for words in the string. 
     // 
     // output: 
     //  cute 
     //  smart 
     foreach (String word in value_trie.AllSubstringValues(s)) 
      Console.WriteLine(word); 

     // Simply test a string for any of the words in the trie. 
     // Note that the Any() operator ensures that the input is no longer 
     // traversed once a single result is found. 
     // 
     // output: 
     //  True 
     Console.WriteLine(simple_trie.AllSubstringValues(s).Any(e=>e)); 

     s = "Four score and seven years ago."; 
     // output: 
     //  False 
     Console.WriteLine(simple_trie.AllSubstringValues(s).Any(e => e)); 
    } 
} 

class TrieNode<TValue> 
{ 
    private TrieNode<TValue>[] nodes = null; 
    private TValue m_value = default(TValue); 
    private Char m_base; 

    public Char Base { get { return m_base; } } 
    public bool IsEnd { get { return !m_value.Equals(default(TValue)); } } 

    public TValue Value 
    { 
     get { return m_value; } 
     set { m_value = value; } 
    } 

    public IEnumerable<TrieNode<TValue>> Nodes { get { return nodes; } } 

    public TrieNode<TValue> this[char c] 
    { 
     get 
     { 
      if (nodes != null && m_base <= c && c < m_base + nodes.Length) 
       return nodes[c - m_base]; 
      return null; 
     } 
    } 

    public TrieNode<TValue> AddChild(char c) 
    { 
     if (nodes == null) 
     { 
      m_base = c; 
      nodes = new TrieNode<TValue>[1]; 
     } 
     else if (c >= m_base + nodes.Length) 
     { 
      Array.Resize(ref nodes, c - m_base + 1); 
     } 
     else if (c < m_base) 
     { 
      Char c_new = (Char)(m_base - c); 
      TrieNode<TValue>[] tmp = new TrieNode<TValue>[nodes.Length + c_new]; 
      nodes.CopyTo(tmp, c_new); 
      m_base = c; 
      nodes = tmp; 
     } 

     TrieNode<TValue> node = nodes[c - m_base]; 
     if (node == null) 
     { 
      node = new TrieNode<TValue>(); 
      nodes[c - m_base] = node; 
     } 
     return node; 
    } 
}; 

class Trie<TValue> : System.Collections.IEnumerable 
{ 
    private TrieNode<TValue> _root = new TrieNode<TValue>(); 

    // This dummy enables C# 3.0 initialization syntax 
    public System.Collections.IEnumerator GetEnumerator() 
    { 
     return null; 
    } 

    public void Add(String s, TValue v) 
    { 
     TrieNode<TValue> node = _root; 
     foreach (Char c in s) 
      node = node.AddChild(c); 

     node.Value = v; 
    } 

    public bool Contains(String s) 
    { 
     TrieNode<TValue> node = _root; 
     foreach (Char c in s) 
     { 
      node = node[c]; 
      if (node == null) 
       return false; 
     } 
     return node.IsEnd; 
    } 

    public TValue Find(String s_in) 
    { 
     TrieNode<TValue> node = _root; 
     foreach (Char c in s_in) 
     { 
      node = node[c]; 
      if (node == null) 
       return default(TValue); 
     } 
     return node.Value; 
    } 

    public IEnumerable<TValue> FindAll(String s_in) 
    { 
     TrieNode<TValue> node = _root; 
     foreach (Char c in s_in) 
     { 
      node = node[c]; 
      if (node == null) 
       break; 
      if (node.Value != null) 
       yield return node.Value; 
     } 
    } 

    public IEnumerable<TValue> AllSubstringValues(String s) 
    { 
     int i_cur = 0; 
     while (i_cur < s.Length) 
     { 
      TrieNode<TValue> node = _root; 
      int i = i_cur; 
      while (i < s.Length) 
      { 
       node = node[s[i]]; 
       if (node == null) 
        break; 
       if (node.Value != null) 
        yield return node.Value; 
       i++; 
      } 
      i_cur++; 
     } 
    } 
}; 
+0

On dirait qu'il pourrait être plus facile et plus efficace d'utiliser Regex avec String.Join ("|", args) comme expression. – user1664043

+0

@ user1664043 'RegEx's sont une approche simple, mais la complexité qui en résulte n'est parfois pas évidente ou transparente. Ma solution, comme indiqué dans la première ligne de mon article, peut être plus appropriée si la performance est critique, car elle offre des garanties transparentes sur la complexité du temps. –

+0

Pour être précis, 'IndexOfAny (String [])' n'est pas vraiment _O (n^2) _ mais plutôt _O (n * m) _, avec _n_ étant la taille de la chaîne dans laquelle la recherche a lieu, et _m_ étant la longueur (maximale) des chaînes recherchées, en supposant que le nombre de chaînes est petit par rapport à _n_ et _m_. –

6

Si quelqu'un trouve autre cela en essayant de rechercher une méthode .Net comme String.IndexOfAny (String []), voici ma solution:

C#

public int IndexOfAny(string test, string[] values) 
{ 
int first = -1; 
foreach (string item in values) { 
    int i = test.IndexOf(item); 
    if (i >= 0) { 
     if (first > 0) { 
      if (i < first) { 
       first = i; 
      } 
     } else { 
      first = i; 
     } 
    } 
} 
return first; 
} 

VB

Public Function IndexOfAny(test As String, values As String()) As Integer 
     Dim first As Integer = -1 
     For Each item As String In values 
      Dim i As Integer = test.IndexOf(item) 
      If i >= 0 Then 
       If first > 0 Then 
        If i < first Then 
         first = i 
        End If 
       Else 
        first = i 
       End If 
      End If 
     Next 
     Return first 
    End Function 

Vous pouvez faire une LastIndexOfAny (String []) en passant juste la

i < first 

à

i > first 
+0

Dans la 6ème ligne, "if (i> 0)" - cela devrait être "if (i> = 0)". – RenniePet

+0

Oh, bon point merci :) – Laurence

Questions connexes