2010-09-14 13 views
13

Ho una lista di parole che voglio ignorare come questo:stringa di sostituire con un elenco <string>

public List<String> ignoreList = new List<String>() 
     { 
      "North", 
      "South", 
      "East", 
      "West" 
     }; 

Per una data stringa, dire "14th Avenue North" voglio essere in grado di rimuovere la parte "Nord", quindi fondamentalmente una funzione che restituirebbe "14th Avenue " quando chiamato.

Mi sembra che ci sia qualcosa che dovrei essere in grado di fare con un mix di LINQ, regex e sostituzioni, ma non riesco a capirlo.

L'immagine più grande è, sto cercando di scrivere un algoritmo di corrispondenza degli indirizzi. Voglio filtrare parole come "Street", "North", "Boulevard", ecc. Prima di utilizzare l'algoritmo Levenshtein per valutare la similarità.

+1

Ma non è una riga @htw. non ottieni punti geek se non è una linea. –

+8

Non lasciare che questo programma venga eseguito a Charlotte, NC. I nomi delle strade di spicco sono East Blvd, South Blvd, West Blvd. Quelli sono i nomi delle strade, non una differenziazione di * ora sei su West 1st Street. * In quella nota, ci sono altri scenari in cui le tue indicazioni non sono realmente indicazioni, ma parti chiave dell'identificatore. Northampton, Northlake (centro commerciale/area di Charlotte), North Carolina, North Dakota, ecc. –

+0

@Anthony: Questo è vero, starò attento a ciò che ho inserito nel mio dizionario. Tuttavia, per prima cosa combacia con il codice postale (zip) che deve corrispondere esattamente alla funzione per considerare anche gli indirizzi. Da lì, non mi importa se preferirei ottenere dei falsi positivi per poi perdere i risultati. –

risposta

12

ne dite di questo:

string.Join(" ", text.Split().Where(w => !ignoreList.Contains(w))); 

o per .Net 3:

string.Join(" ", text.Split().Where(w => !ignoreList.Contains(w)).ToArray()); 

Nota che questo metodo divide la stringa in singole parole in modo che rimuove solo parole intere. In questo modo funzionerà correttamente con indirizzi come Northampton Way #123 che string.Replace non può gestire.

+0

* sorso * - ha un sapore come Perl! –

+0

Questa è un'ottima soluzione, sia più breve che più chiara delle versioni regex. – AHM

+0

Puoi anche dividere le parole - 'text.Split (ignoreList.ToArray(), StringSplitOptions.None)'. Detto questo, è più facile adattare il tuo approccio per ignorare il caso. – Kobi

2

Qualcosa del genere dovrebbe funzionare:

string FilterAllValuesFromIgnoreList(string someStringToFilter) 
{ 
    return ignoreList.Aggregate(someStringToFilter, (str, filter)=>str.Replace(filter, "")); 
} 
+1

Sospetto che sia corretto, eppure in realtà non lo so. –

+1

Potrei aver scambiato i parametri con il secondo lambda ma questo funzionerà sicuramente, Aggregate è un metodo incredibilmente potente, le sue persone zoppo non lo usano molto spesso –

+1

Va notato che dubito che la chiamata Sostituisci più volte non sia il modo più preformante per farlo. Probabilmente qualcosa in cui si costruisce il contenuto della lista in una RegEx statica e si usa quella per sostituire sarebbe più veloce, ma sospetto che la differenza non contenga in questo caso. –

0
public static string Trim(string text) 
{ 
    var rv = text; 
    foreach (var ignore in ignoreList) 
     rv = rv.Replace(ignore, ""); 
    return rv; 
} 

aggiornato per Gabe


public static string Trim(string text) 
{ 
    var rv = ""; 
    var words = text.Split(" "); 
    foreach (var word in words) 
    { 
     var present = false; 
     foreach (var ignore in ignoreList) 
     if (word == ignore) 
      present = true; 
     if (!present) 
     rv += word; 
    } 
    return rv; 
} 
+0

No LINQ, non RegExp, eppure è corretto. L'unica cosa che cambierei è l'uso di una stringa letterale vuota. –

+7

No, non corretto. Questo trasformerà "123 Northampton" in "123 ampton". – Gabe

+0

Chiudi ... ora è necessario assicurarsi di riposizionare lo spazio tra le parole. – Gabe

2

Cosa c'è di sbagliato con un semplice ciclo for?

string street = "14th Avenue North"; 
foreach (string word in ignoreList) 
{ 
    street = street.Replace(word, string.Empty); 
} 
0

Se si dispone di un elenco, penso che si dovrà toccare tutti gli elementi. Puoi creare un imponente RegEx con tutte le parole chiave da ignorare e sostituirlo con String.Empty.

Ecco un inizio:

(^|\s+)(North|South|East|West){1,2}(ern)?(\s+|$) 

Se si dispone di un unico RegEx per ignorare le parole, si può fare una sola sostituzione per ogni frase che si desidera passare al algoritmo.

+0

Credo che potremmo. Lo vogliamo davvero? –

+0

Questo è un buon inizio. Ora fallo in modo che corrisponda solo a parole intere. – Gabe

+0

Abbiamo utilizzato questo approccio per contrassegnare un elenco enorme di clienti come aziendali o residenziali in base alle parole chiave RegEx generate dall'esame dei dati. – Brad

6
Regex r = new Regex(string.Join("|", ignoreList.Select(s => Regex.Escape(s)).ToArray())); 
string s = "14th Avenue North"; 
s = r.Replace(s, string.Empty); 
+1

se ci sono caratteri speciali, dovresti sfuggire alla roba in ignoreList: string.Join ("|", ignoreList.select (s => Regex.Escape (s)). ToArray()) –

+0

Poiché le probabilità sono che l'elenco conterrà parole come '" St. "', si consiglia di scappare. E devi cercare solo parole intere. – Gabe

+1

@Frank corretto. . . sebbene non sia realmente specificato da dove proviene la lista. Sarebbe probabilmente più semplice scrivere semplicemente l'espressione regolare corretta in primo luogo piuttosto che convertirla da una lista, a meno che la lista non sia realmente necessaria. – Bob

0

Perché non juts Mantenerlo semplice?

public static string Trim(string text) 
{ 
    var rv = text.trim(); 
    foreach (var ignore in ignoreList) { 
     if(tv.EndsWith(ignore) { 
     rv = rv.Replace(ignore, string.Empty); 
    } 
    } 
    return rv; 
} 
1

Se si tratta di una stringa breve come nell'esempio, è possibile eseguire il ciclo delle stringhe e sostituirle una alla volta. Se si desidera ottenere fantasia è possibile utilizzare il metodo LINQ aggregato per farlo:

address = ignoreList.Aggregate(address, (a, s) => a.Replace(s, String.Empty)); 

Se è una stringa di grandi dimensioni, che sarebbe lento. Invece è possibile sostituire tutte le stringhe in un'unica esecuzione attraverso la stringa, che è molto più veloce. Ho fatto un metodo per questo in this answer.

+0

Grazie mille per quello. La mia lista ignorata sarà ovviamente molto più lunga di quella che ho postato qui, ma non sono sicuro che sarà abbastanza lunga per usare il tuo metodo. Lo descriverò e vedrò comunque. –

2

Se si sa che l'elenco di parola contiene solo i caratteri che non necessitano di fuga all'interno di un'espressione regolare, allora si può fare questo:

string s = "14th Avenue North"; 
Regex regex = new Regex(string.Format(@"\b({0})\b", 
         string.Join("|", ignoreList.ToArray()))); 
s = regex.Replace(s, ""); 

Risultato:

 
14th Avenue 

Se ci sono speciali caratteri dovrai correggere due cose:

  • Utilizzare Regex.Escape su ogni elemento dell'elenco ignorato.
  • La parola-confine \b non corrisponde a uno spazio seguito da un simbolo o viceversa. Potrebbe essere necessario verificare gli spazi bianchi (o altri caratteri di separazione come la punteggiatura) usando invece asserzioni di lookaround.

Ecco come risolvere questi due problemi:

Regex regex = new Regex(string.Format(@"(?<= |^)({0})(?= |$)", 
    string.Join("|", ignoreList.Select(x => Regex.Escape(x)).ToArray()))); 
+0

È una buona scommessa che le sue parole * avranno * bisogno di scappare, perché saranno come '" St. "," Blvd. "," Rd. "' – Gabe

+0

Questo è un ottimo modo per gestire il problema dello spazio generato in un altro commento. –

+0

Questo è molto intelligente e sembra che funzioni su tutte le parole. Scriverò alcuni test per questo e provalo correttamente. –

0

È possibile eseguire questa operazione utilizzando e l'espressione, se volete, ma è più facile da girare intorno rispetto all'utilizzo di un aggregato. Vorrei fare qualcosa di simile:

string s = "14th Avenue North" 
ignoreList.ForEach(i => s = s.Replace(i, "")); 
//result is "14th Avenue " 
1

LINQ rende questo facile e leggibile. Ciò richiede tuttavia dati normalizzati, in particolare in quanto è sensibile alla distinzione tra maiuscole e minuscole.

List<string> ignoreList = new List<string>() 
{ 
    "North", 
    "South", 
    "East", 
    "West" 
};  

string s = "123 West 5th St" 
     .Split(' ') // Separate the words to an array 
     .ToList() // Convert array to TList<> 
     .Except(ignoreList) // Remove ignored keywords 
     .Aggregate((s1, s2) => s1 + " " + s2); // Reconstruct the string 
+1

Il '.ToList()' non è necessario. – Gabe

Problemi correlati