2012-11-03 10 views
5

Aktualnie próbuję rozwiązać problem z codingbat.com za pomocą wyrażeń regularnych.Iterowanie za pomocą łańcucha znaków z .find() w języku Java regex

Jestem nowicjuszem, więc docenimy wyjaśnienia krok po kroku. Mógłbym to stosunkowo łatwo rozwiązać za pomocą metod String, ale staram się używać wyrażeń regularnych.

Oto podpowiedź: Dany ciąg znaków i niepusty ciąg wyrazów, zwracają ciąg znaków z każdego znaku tuż przed i tuż po każdym pojawieniu się słowa w ciągu znaków. Ignoruj ​​przypadki, w których przed słowem lub po słowie nie ma znaku, a znak może być zawarty dwa razy, jeśli znajduje się pomiędzy dwoma wyrazami.

wordEnds("abcXY123XYijk", "XY") → "c13i" 
wordEnds("XY123XY", "XY") → "13" 
wordEnds("XY1XY", "XY") → "11" 

itp

Mój kod do tej pory:

String regex = ".?" + word+ ".?"; 
Pattern p = Pattern.compile(regex); 
Matcher m = p.matcher(str); 

String newStr = ""; 
while(m.find()) 
    newStr += m.group().replace(word, ""); 

return newStr; 

Problemem jest to, że gdy istnieje wiele wystąpień słowa w rzędzie, program zdobywa znak poprzedzający słowo ponieważ m. find() rozwija się poza nią.

Na przykład: wordEnds("abc1xyz1i1j", "1") powinien powrócić "cxziij", ale moja metoda zwraca "cxzij", nie powtarzając "i"

chciałbym docenić non-brudny roztwór z wyjaśnieniem mogę zastosować do innych problemów ogólnie regex.

+0

Zobacz tę odpowiedź o wygląd wokół regularnych wyrażeń http: // stackoverflow. com/a/2995621/324900 – Reddy

+0

naprawdę przydatne, dzięki – Rishi

+0

cieszę się z pomocy! :) – Reddy

Odpowiedz

1

To rozwiązanie jedna wkładka:

String wordEnds = input.replaceAll(".*?(.)" + word + "(?:(?=(.)" + word + ")|(.).*?(?=$|." + word + "))", "$1$2$3"); 

Dopasowuje sprawę krawędzi jako antycypowanym w grupie bez przechwytywania, a następnie odpowiada zwykłej (konsumpcji) obudowy.

Należy pamiętać, że twoje wymagania nie wymagają powtórzenia, tylko tytuł pytania zakłada, że ​​jest to konieczne, a nim nie jest.

Należy również zauważyć, że jest to absolutnie bezpieczne, należy uciec wszystkie znaki w word w przypadku żadnej z nich są specjalne znaki „regex”, więc jeśli nie może zagwarantować, że trzeba użyć Pattern.quote(word) zamiast word.

Oto próba zwykłej sprawy i sprawy krawędzi, pokazując to działa:

public static String wordEnds(String input, String word) { 
    word = Pattern.quote(word); // add this line to be 100% safe 
    return input.replaceAll(".*?(.)" + word + "(?:(?=(.)" + word + ")|(.).*?(?=$|." + word + "))", "$1$2$3"); 
} 

public static void main(String[] args) { 
    System.out.println(wordEnds("abcXY123XYijk", "XY")); 
    System.out.println(wordEnds("abc1xyz1i1j", "1")); 
} 

wyjściowa:

c13i 
cxziij 
+0

To nie jest w porządku - mam zamiar wrócić do tego później – Bohemian

+0

teraz dostaję jak skuteczne są regex, dzięki – Rishi

+0

@Bohemian, który jest niepoprawny potrzebuje 'cxziij' jako wyjście nie' cxzi'..to jest powód, dla którego yi użył lookarounds ... – Anirudha

0

Korzystanie pozytywne lookbehind i dodatnią uprzedzona które mają zerową szerokość twierdzenia

(?<=(.)|^)1(?=(.)|$) 
    ^ ^ ^-looks for a character after 1 and captures it in group2 
    |  |->matches 1..you can replace it with any word 
    | 
    |->looks for a character just before 1 and captures it in group 1..this is zero width assertion that doesn't move forward to match.it is just a test and thus allow us to capture the values 

$1 i $2 zawiera swoją value..Go od znalezienia do końca

więc powinno to być jak

String s1 = "abcXY123XYiXYjk"; 
String s2 = java.util.regex.Pattern.quote("XY"); 
String s3 = ""; 
String r = "(?<=(.)|^)"+s2+"(?=(.)|$)"; 
Pattern p = Pattern.compile(r); 
Matcher m = p.matcher(s1); 
while(m.find()) s3 += m.group(1)+m.group(2); 
//s3 now contains c13iij 

działa here

+0

dzięki! prawdopodobnie powinien zrobić trochę więcej czytania ... – Rishi

+4

-1 Waaaaaay zbyt skomplikowane, a tak naprawdę źle. Nie potrzebujesz rozglądać się! Po prostu użyj '(.)' - mówi "nie pasuje, jeśli nie ma postaci", ale kończysz ćwiczenia przez dopasowywanie początku i końca, co w rzeczywistości * nie jest * tym, co OP mówi, że chce – Bohemian

+0

@ Czeski Podobał mi się twoja oryginalna odpowiedź ze względu na jego prostotę, więc byłbym wdzięczny, gdybyś mógł to opublikować (przy pomocy str.replace). – Rishi

0

Użyj regex następująco:

Matcher m = Pattern.compile("(.|)" + Pattern.quote(b) + "(?=(.?))").matcher(a); 
for (int i = 1; m.find(); c += m.group(1) + m.group(2), i++); 

Sprawdź this demo.

Powiązane problemy