Java regex do wyodrębniania tekstu między tagami

82

Mam plik z kilkoma niestandardowymi tagami i chciałbym napisać wyrażenie regularne, aby wyodrębnić ciąg między tagami. Na przykład, jeśli mój tag to:

[customtag]String I want to extract[/customtag]

Jak napisać wyrażenie regularne, aby wyodrębnić tylko ciąg między tagami. Ten kod wydaje się krokiem we właściwym kierunku:

Pattern p = Pattern.compile("[customtag](.+?)[/customtag]");
Matcher m = p.matcher("[customtag]String I want to extract[/customtag]");

Nie wiem, co robić dalej. Jakieś pomysły? Dzięki.

b10hazard
źródło
1
Na początek musisz opuścić []nawiasy kwadratowe, które są metaznakami w wyrażeniu regularnym.
ridgerunner

Odpowiedzi:

186

Jesteś na dobrej drodze. Teraz wystarczy wyodrębnić żądaną grupę w następujący sposób:

final Pattern pattern = Pattern.compile("<tag>(.+?)</tag>", Pattern.DOTALL);
final Matcher matcher = pattern.matcher("<tag>String I want to extract</tag>");
matcher.find();
System.out.println(matcher.group(1)); // Prints String I want to extract

Jeśli chcesz wyodrębnić wiele trafień, spróbuj tego:

public static void main(String[] args) {
    final String str = "<tag>apple</tag><b>hello</b><tag>orange</tag><tag>pear</tag>";
    System.out.println(Arrays.toString(getTagValues(str).toArray())); // Prints [apple, orange, pear]
}

private static final Pattern TAG_REGEX = Pattern.compile("<tag>(.+?)</tag>", Pattern.DOTALL);

private static List<String> getTagValues(final String str) {
    final List<String> tagValues = new ArrayList<String>();
    final Matcher matcher = TAG_REGEX.matcher(str);
    while (matcher.find()) {
        tagValues.add(matcher.group(1));
    }
    return tagValues;
}

Zgadzam się jednak, że wyrażenia regularne nie są tutaj najlepszą odpowiedzią. Użyłbym XPath do znalezienia interesujących mnie elementów. Zobacz API Java XPath po więcej informacji.

hoipolloi
źródło
3
Wielkie dzięki, właśnie tego potrzebowałem. Przyjrzę się XPaths, ale na razie myślę, że to rozwiązanie zadziała. Moje aplikacje są bardzo proste i prawdopodobnie takie pozostaną. Dzięki jeszcze raz!
b10hazard
A co z tym sznurkiem "<tag>apple</tag><b>hello</b><tag>orange</tag><tag>pear"? Jak możemy się pearobejść bez tagu?
K.Sopheak
Aby uogólnić: private String extractDataFromTags (tag String) {Pattern pattern = Pattern.compile ("<. +?> (. +?) </.+?>"); Matcher matcher = pattern.matcher (tag); matcher.find (); return (matcher.group (1)); // Wyświetla łańcuch, który chcę wyodrębnić lub zgłasza wyjątek}
PMateus,
15

Szczerze mówiąc, wyrażenia regularne nie są najlepszym pomysłem na tego typu analizowanie. Opublikowane przez Ciebie wyrażenie regularne prawdopodobnie będzie dobrze działać w prostych przypadkach, ale jeśli sprawy staną się bardziej złożone, będziesz mieć ogromne problemy (z tego samego powodu, dla którego nie możesz niezawodnie przeanalizować HTML za pomocą wyrażeń regularnych). Wiem, że prawdopodobnie nie chcesz tego słyszeć. Wiem, że tego nie robiłem, gdy zadawałem pytania tego samego typu, ale analizowanie ciągów znaków stało się dla mnie O WIELE bardziej niezawodne, gdy przestałem używać wyrażeń regularnych do wszystkiego.

jTopas to NIESAMOWITY tokenizer, który bardzo ułatwia ręczne pisanie parserów (MOCNIE sugeruję użycie jtopa zamiast standardowych bibliotek skanera java / etc ..). Jeśli chcesz zobaczyć jtopas w akcji, oto kilka parserów, które napisałem przy użyciu jTopas do parsowania tego typu pliku

Jeśli analizujesz pliki XML, powinieneś używać biblioteki parsera xml. Nie rób tego sam, chyba że robisz to tylko dla przyjemności, istnieje wiele sprawdzonych opcji

jdc0589
źródło
Dzieki za sugestie. Dodałem je do zakładek i na pewno rozważę ich wykorzystanie w przyszłych projektach. Na razie metoda regex jest prawdopodobnie tą, z której będę korzystać, ponieważ plik, który analizuję, jest bardzo mały / prosty.
b10hazard
7

Ogólne, prostsze i nieco prymitywne podejście do znajdowania znacznika, atrybutu i wartości

    Pattern pattern = Pattern.compile("<(\\w+)( +.+)*>((.*))</\\1>");
    System.out.println(pattern.matcher("<asd> TEST</asd>").find());
    System.out.println(pattern.matcher("<asd TEST</asd>").find());
    System.out.println(pattern.matcher("<asd attr='3'> TEST</asd>").find());
    System.out.println(pattern.matcher("<asd> <x>TEST<x>asd>").find());
    System.out.println("-------");
    Matcher matcher = pattern.matcher("<as x> TEST</as>");
    if (matcher.find()) {
        for (int i = 0; i <= matcher.groupCount(); i++) {
            System.out.println(i + ":" + matcher.group(i));
        }
    }
Gorky
źródło
Jaki byłby wzorzec, gdyby istniała sekwencja różnych tagów lub tagów zagnieżdżonych, takich jak <h2>Mac</h2><h1>loves it</h1>lub <h2>Mac<h1>liked your answer</h1></h2>?
MAC
1
proszę edytować i <matcher.groupCount (); to i <= matcher.groupCount (); aby uwzględnić pierwszy pasujący podciąg, tj. indeks 0
AVA
4

Spróbuj tego:

Pattern p = Pattern.compile(?<=\\<(any_tag)\\>)(\\s*.*\\s*)(?=\\<\\/(any_tag)\\>);
Matcher m = p.matcher(anyString);

Na przykład:

String str = "<TR> <TD>1Q Ene</TD> <TD>3.08%</TD> </TR>";
Pattern p = Pattern.compile("(?<=\\<TD\\>)(\\s*.*\\s*)(?=\\<\\/TD\\>)");
Matcher m = p.matcher(str);
while(m.find()){
   Log.e("Regex"," Regex result: " + m.group())       
}

Wynik:

10 Ene

3,08%

Heriberto Rivera
źródło
2
    final Pattern pattern = Pattern.compile("tag\\](.+?)\\[/tag");
    final Matcher matcher = pattern.matcher("[tag]String I want to extract[/tag]");
    matcher.find();
    System.out.println(matcher.group(1));
Bibhuti Agarwal
źródło
co powiesz na prefiks dla tagu (jeśli prefiks jest dynamiczny)
user1514499
2
    String s = "<B><G>Test</G></B><C>Test1</C>";

    String pattern ="\\<(.+)\\>([^\\<\\>]+)\\<\\/\\1\\>";

       int count = 0;

        Pattern p = Pattern.compile(pattern);
        Matcher m =  p.matcher(s);
        while(m.find())
        {
            System.out.println(m.group(2));
            count++;
        }
Shubham Khurana
źródło
1

Przedrostuję tę odpowiedź słowami "nie powinieneś używać wyrażenia regularnego do analizowania XML - spowoduje to tylko przypadki skrajne, które nie działają poprawnie, oraz wyrażenie regex o stale rosnącej złożoności, gdy spróbujesz to naprawić . ”

Biorąc to pod uwagę, musisz kontynuować, dopasowując ciąg i chwytając żądaną grupę:

if (m.matches())
{
   String result = m.group(1);
   // do something with result
}
Shirik
źródło