2009-08-31 13 views
40

Należy rozważyć wymóg znalezienia dopasowanej pary zestawów znaków i usunąć między nimi znaki, , a także te znaki/ograniczniki.Usunąć tekst pomiędzy ogranicznikami w ciągu znaków (używając wyrażenia regularnego?)

Oto zestawy ograniczniki:

[] square brackets 
() parentheses 
"" double quotes 
'' single quotes 

Oto niektóre przykłady łańcuchów, które powinny pasować:

Given:      Results In: 
------------------------------------------- 
Hello "some" World   Hello World 
Give [Me Some] Purple  Give Purple 
Have Fifteen (Lunch Today) Have Fifteen 
Have 'a good'day    Have day 

Oraz kilka przykładów ciągów, które nie powinny pasować:

Does Not Match: 
------------------ 
Hello "world 
Brown]co[w 
Cheese'factory 

Jeśli podany ciąg nie zawiera pasującego zestawu ograniczników, nie zostanie zmodyfikowany. Ciąg wejściowy może zawierać wiele pasujących par ograniczników. Jeśli zestaw 2 ograniczników nakłada się (tj. he[llo "worl]d"), byłby to przypadek skrajny, który możemy zignorować tutaj.

Algorytm będzie wyglądać mniej więcej tak:

string myInput = "Give [Me Some] Purple (And More) Elephants"; 
string pattern; //some pattern 
string output = Regex.Replace(myInput, pattern, string.Empty); 

Pytanie: W jaki sposób można to osiągnąć z C#? Jestem skłonny do regex.

Bonus: Czy istnieją proste sposoby na dopasowanie ograniczników początkowych i końcowych do stałych lub na jakiejś liście? Rozwiązanie, którego szukam, byłoby łatwe do zmiany ograniczników na wypadek, gdyby analitycy biznesowi wymyślili nowe zestawy ograniczników.

Odpowiedz

35

Proste regex byłoby:

string input = "Give [Me Some] Purple (And More) Elephants"; 
string regex = "(\\[.*\\])|(\".*\")|('.*')|(\\(.*\\))"; 
string output = Regex.Replace(input, regex, ""); 

Jak by to zrobić niestandardowy sposób, w którym chcesz zbudować regex byłby po prostu trzeba zbudować części:

('.*') // example of the single quote check 

wtedy każda pojedyncza część regex połączona z operatorem OR (wyrażenie | w regex), tak jak w moim oryginalnym przykładzie. Gdy masz już gotowy łańcuch regex, uruchom go raz. Kluczem jest sprawienie, aby wyrażenie regularne było jednym sprawdzeniem, ponieważ wykonanie wielu dopasowań wyrażenia regularnego na jednym elemencie, a następnie iteracja wielu elementów prawdopodobnie spowoduje znaczny spadek wydajności.

W pierwszym przykładzie, że zajmie miejsce następujący wiersz:

string input = "Give [Me Some] Purple (And More) Elephants"; 
string regex = "Your built up regex here"; 
string sOutput = Regex.Replace(input, regex, ""); 

Jestem pewien, że ktoś będzie po chłodnym linq wyrażenia zbudować regex w oparciu o tablicę obiektów ogranicznika do meczu lub czegoś .

+2

To będzie teraz działało jako (najprawdopodobniej) oczekiwane dla "Give [Me Some] Purple (i więcej) [Big] Elephants". Można to rozwiązać, używając ". *?" zamiast ". *" w wyrażeniu podanym powyżej. – mayu

9

Muszę dodać stare powiedzenie: "Masz problem i chcesz używać wyrażeń regularnych, teraz masz dwa problemy."

mam wymyślić szybki regex, które miejmy nadzieję pomóc w kierunku szukasz:

[.]*(\(|\[|\"|').*(\]|\)|\"|')[.]* 

nawiasem, wsporniki, podwójne cudzysłowy są uciekł, gdy apostrof może być pozostawiona sam.

Aby umieścić powyższe wyrażenie w języku angielskim, dopuszcza się dowolną liczbę znaków przed i dowolną liczbę po, dopasowując wyrażenie między dopasowanymi ogranicznikami.

Otwarte wyrażenie ograniczające to (\(|\[|\"|') To pasuje do zamykającej frazy. Aby uczynić to bardziej rozszerzalnym w przyszłości, możesz usunąć faktyczne separatory i zawrzeć je w pliku konfiguracyjnym, bazie danych lub w dowolnym miejscu.

+0

+1 regex wydaje się robić to, czego potrzebuje. Wystarczy prosty regex.Replace jest potrzebny, aby go zaokrąglić. – James

+5

bump dla "... Teraz masz dwa problemy.", LOL –

34

Prostym sposobem byłoby to zrobić:

string RemoveBetween(string s, char begin, char end) 
{ 
    Regex regex = new Regex(string.Format("\\{0}.*?\\{1}", begin, end)); 
    return regex.Replace(s, string.Empty); 
} 

string s = "Give [Me Some] Purple (And More) \\Elephants/ and .hats^"; 
s = RemoveBetween(s, '(', ')'); 
s = RemoveBetween(s, '[', ']'); 
s = RemoveBetween(s, '\\', '/'); 
s = RemoveBetween(s, '.', '^'); 

Zmiana komunikatu zwrotnego do poniżej pozwoli uniknąć duplikatów puste przestrzenie:

return new Regex(" +").Replace(regex.Replace(s, string.Empty), " "); 

Końcowym rezultatem tego będzie:

"Give Purple and " 

Disclamer: Pojedynczy regex może prawdopodobnie szybciej niż to.

+3

Program operacyjny nie zawierał wzmianki o "i kapeluszach". "Daj mi fiolet i więcej słoni", o co wyraźnie prosił OP. Dlaczego wykręciłeś jego słowa i dodałeś czapki do równania? –

+1

+1. Odnalazłem się w tym wątku i nie zdawałem sobie sprawy, że opublikowałem powyższy komentarz! Słaba próba humoru. Dzięki za odpowiedź. –

+0

Dlaczego czapki ?! Myślę, że to moja własna zła próba humoru;). Cieszę się, że to nadal jest przydatne. –

0

Poniższa Regex

(\{\S*\}) 

Co to wyrażenie regularne robi to zastępuje żadnych wystąpień {słowo} z modifiedWord chcesz go zastąpić.

Niektóre przykładowy kod C#:

static readonly Regex re = new Regex(@"(\{\S*\})", RegexOptions.Compiled); 
     /// <summary> 
     /// Pass text and collection of key/value pairs. The text placeholders will be substituted with the collection values. 
     /// </summary> 
     /// <param name="text">Text that containes placeholders such as {fullname}</param> 
     /// <param name="fields">a collection of key values pairs. Pass <code>fullname</code> and the value <code>Sarah</code>. 
     /// DO NOT PASS keys with curly brackets <code>{}</code> in the collection.</param> 
     /// <returns>Substituted Text</returns> 
     public static string ReplaceMatch(this string text, StringDictionary fields) 
     { 
      return re.Replace(text, match => fields[match.Groups[1].Value]); 
     } 

w zdaniu takich jak

Regex Hero jest w czasie rzeczywistym {{Internecie Silverlight}} Regular Expression Tester.

Zastąpi on tylko {Silverlight} i nie wychodząc z pierwszego {wspornika do ostatniego} wsporniku.

2

Opierając się na Bryan Menard's regular expression, zrobiłem metodę rozszerzenia, które będą również pracować dla zagnieżdżonych zamienników jak "[Test 1 [[Test2] Test3]] Hello World":

/// <summary> 
    /// Method used to remove the characters betweeen certain letters in a string. 
    /// </summary> 
    /// <param name="rawString"></param> 
    /// <param name="enter"></param> 
    /// <param name="exit"></param> 
    /// <returns></returns> 
    public static string RemoveFragmentsBetween(this string rawString, char enter, char exit) 
    { 
     if (rawString.Contains(enter) && rawString.Contains(exit)) 
     { 
      int substringStartIndex = rawString.IndexOf(enter) + 1; 
      int substringLength = rawString.LastIndexOf(exit) - substringStartIndex; 

      if (substringLength > 0 && substringStartIndex > 0) 
      { 
       string substring = rawString.Substring(substringStartIndex, substringLength).RemoveFragmentsBetween(enter, exit); 
       if (substring.Length != substringLength) // This would mean that letters have been removed 
       { 
        rawString = rawString.Remove(substringStartIndex, substringLength).Insert(substringStartIndex, substring).Trim(); 
       } 
      } 

      //Source: https://stackoverflow.com/a/1359521/3407324 
      Regex regex = new Regex(String.Format("\\{0}.*?\\{1}", enter, exit)); 
      return new Regex(" +").Replace(regex.Replace(rawString, string.Empty), " ").Trim(); // Removing duplicate and tailing/leading spaces 
     } 
     else 
     { 
      return rawString; 
     } 
    } 

Wykorzystanie tej metody byłoby w sugerowany przypadek wygląda następująco:

string testString = "[Test 1 [[Test2] Test3]] Hello World"; 
testString.RemoveFragmentsBetween('[',']'); 

Zwrot ciąg "Hello World".

+0

Złoto! Dziękuję Ci! –

Powiązane problemy