2010-09-14 9 views
13

ich eine Liste von Worten habe ich wie diese ignorieren will:Zeichenfolge ersetzen mit einer Liste <string>

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

Für eine gegebene Zeichenfolge, sagen "14th Avenue North" Ich will den „Nord“ Teil entfernen zu können, also im Grunde eine Funktion, die "14th Avenue " zurückgeben würde, wenn sie aufgerufen wird.

Ich habe das Gefühl, dass ich etwas mit LINQ, Regex und Replace machen kann, aber ich kann es einfach nicht herausfinden.

Das größere Bild ist, ich versuche, einen Adressenabgleich-Algorithmus zu schreiben. Ich möchte Wörter wie "Street", "North", "Boulevard" usw. herausfiltern, bevor ich den Levenshtein-Algorithmus zur Bewertung der Ähnlichkeit verwende.

+1

Aber es ist nicht eine Zeile @htw. Sie erhalten keine Geek-Punkte, wenn es nicht eine Zeile ist. –

+8

Lassen Sie dieses Programm nicht in Charlotte, NC laufen. Prominente Straßennamen sind zufällig East Blvd, South Blvd, West Blvd. Das sind die Namen der Straßen, keine Unterscheidung von * jetzt sind Sie auf West 1st Street. * In diesem Sinne gibt es andere Szenarien, wo Ihre Richtungen nicht wirklich Richtungen, sondern Schlüsselbereiche der Kennung sind. Northampton, Northlake (Einkaufszentrum/Bereich in Charlotte), North Carolina, North Dakota usw. –

+0

@Anthony: Das ist wahr, ich werde vorsichtig sein mit dem, was ich in mein Wörterbuch einlege. Allerdings passe ich zuerst mit der Postleitzahl (zip) an, die genau für die Funktion übereinstimmen muss, um auch die Adressen zu berücksichtigen. Von da an macht es mir nichts aus, wenn ich lieber falsche Ergebnisse bekomme, als Ergebnisse zu verpassen. –

Antwort

12

Wie wäre es damit:

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

oder für .Net 3:

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

Beachten Sie, dass diese Methode die Zeichenfolge in einzelne Wörter zerlegt, so dass nur das ganze Wörter entfernt. Auf diese Weise funktioniert es richtig mit Adressen wie Northampton Way #123, die string.Replace nicht verarbeiten kann.

+0

* Schluck * - schmeckt wie Perl! –

+0

Dies ist eine großartige Lösung, sowohl kürzer als auch klarer als die Regex-Versionen. – AHM

+0

Sie können auch durch die Wörter - 'text.Split (ignoreList.ToArray(), StringSplitOptions.None)' teilen. Das heißt, es ist einfacher, Ihre Herangehensweise anzupassen, um den Fall zu ignorieren. – Kobi

2

So etwas sollte funktionieren:

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

Ich vermute, das ist richtig, und doch weiß ich es nicht. –

+1

ich um die Parameter das zweite Lambda vertauscht könnte, aber dies wird auf jeden Fall funktioniert, ist Aggregat eine unglaublich leistungsfähige Methode, seine lahm Leute es nicht sehr oft –

+1

verwenden Es sollte beachtet werden, dass ich bezweifle, dass sie mehrmals ersetzen Aufruf ist nicht die präformanteste Art, dies zu tun. Wahrscheinlich etwas, wo Sie den Inhalt der Liste in eine statische RegEx erstellen und verwenden, um zu ersetzen, wäre schneller, aber ich vermute, dass der Unterschied in diesem Fall keine Rolle spielt. –

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

Aktualisiert 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

Kein LINQ, nicht RegExp, aber es ist korrekt. Einzige Sache, die ich ändern würde, ist die Verwendung eines leeren String-Literals. –

+7

Nein, nicht korrekt. Dies wird "123 Northampton" in "123 ampton" verwandeln. – Gabe

+0

Schließen ... Jetzt müssen Sie sicherstellen, dass Sie den Abstand zwischen Wörtern zurückstellen. – Gabe

2

Was für Schleife mit einem einfachen falsch?

0

Wenn Sie eine Liste haben, denke ich, dass Sie alle Elemente berühren müssen. Sie können eine massive RegEx mit all Ihren ignorierenden Keywords erstellen und durch ersetzen.

Hier ist ein Anfang:

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

Wenn Sie ein einzelnes RegEx haben für Worte ignorieren, können Sie für jede Phrase eine einzige ersetzen möchten Sie dem Algorithmus übergeben wollen.

+0

Ich denke, wir könnten. Wollen wir das wirklich? –

+0

Das ist ein guter Anfang. Jetzt mach es so, dass es nur ganzen Wörtern entspricht. – Gabe

+0

Wir verwendeten diesen Ansatz, um eine riesige Liste von Kunden als geschäftlich oder privat zu kennzeichnen, basierend auf RegEx-Schlüsselwörtern, die beim Betrachten der Daten generiert wurden. – 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

Wenn es Sonderzeichen gibt, sollten Sie das Zeug in ignoreList: string.Join ("|", ignoreList.select (s => Regex.Escape (s)). ToArray()) –

+0

Da die Chancen sind die Liste enthalten wird Wörter wie "St.", ist es ratsam zu entkommen. Und du musst nur nach ganzen Wörtern suchen. – Gabe

+1

@Frank korrekt. . . obwohl es nicht wirklich angegeben ist, woher die Liste kommt. Es wäre wahrscheinlich am einfachsten, einfach den richtigen regulären Ausdruck zu schreiben, anstatt ihn aus einer Liste zu konvertieren, es sei denn, die Liste ist wirklich notwendig. – Bob

0

Warum halten nicht juts es einfach?

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

Wenn es sich um eine kurze Zeichenfolge wie in Ihrem Beispiel handelt, können Sie nur die Zeichenfolgen durchlaufen und einzeln ersetzen. Wenn Sie Lust bekommen möchten, können Sie die LINQ Aggregate Methode verwenden, um es zu tun:

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

Wenn es eine große Zeichenfolge ist, die langsam sein würde. Stattdessen können Sie alle Zeichenfolgen in einem einzigen Durchlauf durch die Zeichenfolge ersetzen, die viel schneller ist. Ich habe dafür eine Methode in this answer gemacht.

+0

Vielen Dank dafür. Meine Ignorierliste wird natürlich viel länger sein als die, die ich hier gepostet habe, aber ich bin mir nicht sicher, ob sie lang genug sein wird, um Ihre Methode zu verwenden. Ich werde es profilieren und sehen. –

2

Wenn Sie wissen, dass die Liste des Wortes nur Zeichen enthält, die in einem regulären Ausdruck zu entkommen nicht brauchen, dann können Sie dies tun:

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

Ergebnis:

 
14th Avenue 

Wenn es etwas Besonderes ist Zeichen müssen Sie zwei Dinge zu beheben:

  • Verwenden Sie Regex.Escape auf jedes Element der Ignorierliste.
  • Die Wortgrenze \b wird keine Leerzeichen durch ein Symbol, oder umgekehrt, gefolgt entsprechen. Möglicherweise müssen Sie stattdessen Whitespace (oder andere trennende Zeichen wie Interpunktion) mithilfe von Lookaround Assertions suchen.

Hier ist, wie diese beiden Probleme zu beheben:

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

Es ist eine ziemlich gute Wette, dass seine Worte * entkommen * müssen, weil sie wie "St.", "Blvd.", "Rd." Sind. – Gabe

+0

Das ist eine großartige Möglichkeit, mit dem Platzproblem in einem anderen umzugehen Kommentar. –

+0

Dies ist sehr clever und es scheint, als würde es bei allen Wörtern funktionieren. Ich werde einige Tests dafür schreiben und es richtig ausprobieren. –

0

Sie können dies mit und Ausdruck tun, wenn Sie mögen, aber es ist einfacher, es zu drehen als ein Aggregat mit. Ich würde so etwas tun:

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

LINQ macht dies einfach und lesbar. Dies erfordert jedoch normalisierte Daten, insbesondere dahingehend, dass zwischen Groß- und Kleinschreibung unterschieden wird.

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

Die '.ToList()' ist nicht notwendig. – Gabe

Verwandte Themen