# [Regex] Zeichen ausschließen



## Tobias (21. Jul 2008)

Hi,

ich habe folgendes Pattern:


```
(\[[(.+?)(=(.+?))?\s?+/\])
```

Das matcht so Sachen wie [br /] oder auch [br=wide/], gebraucht wird es für einen BBCode-Interpreter. Nun gibt es aber folgendes Problem:

Der Text



> [h1]Überschrift[/h1][br /]Satz 1[br /]Satz 2[br /][imp]Wichtiger Satz 3[/imp]



wird von obigem Regex wie folgt aufgeteilt:


[h1]Überschrift[/h1][br /]
[br /]
[br /]

der erste Treffer ist also viel zu lang, weil die öffnende Klammer des [h1] genommen wird und nicht die öffnende Klammer von [br/]. Da ich bereits den Gebrauch von reluctant quantifiers praktiziere (was mir hier nichts nutzt, weil von vorne nach hinten gematcht wird), fällt mir als Lösung nur ein, dem Pattern zu verbieten, "[" oder "]" zu matchen, ausgenommen letzteres wird von einem "/" eingeleitet. Aber wie bringe ich dem Pattern bei, dass diese Zeichen nirgends im Match auftauchen dürfen außer als erstes und letztes Zeichen?

mpG
Tobias


----------



## SlaterB (22. Jul 2008)

'ein kompilierbares, _kurzes_ Beispiel, das das von dir beschriebene Verhalten zeigt.'
wäre hier auch nicht schlecht..
( http://www.java-forum.org/de/viewtopic.php?t=72400 )

dein Pattern ist gar nicht kompilierbar, es enthält zwei öffnende eckige Klammern, aber nur eine schließende

denkbar ist:


```
public class Test {

	public static void main(String[] args) throws Exception {

		String pattern = "(\\[([^\\[\\]]*)/\\])";
		Pattern p = Pattern.compile(pattern);
		Matcher m = p.matcher("d[h1]Überschrift[/h1][br /]");
		while (m.find()) {
			System.out.println(m.group(1));
			System.out.println(m.group(2));
		}
	}
}
```


----------



## Tobias (22. Jul 2008)

Kleines kompilierbares Beispiel (war spät gestern, sorry):


```
public class RegexTester {

	public static final String TEXT = "[h1]Überschrift[/h1][br /]Satz 1[br /]Satz 2[br /][imp]Wichtiger Satz 3[/imp]";

	public static final String PATTERN = "(\\[(.+?)(=(.+?))?\\s?+/\\])";

	private Matcher matcher;

	public RegexTester() {
		matcher = Pattern.compile(PATTERN).matcher(TEXT);
	}

	public boolean matches() {
		return matcher.find();
	}

	/**
	 * @return a list of all matches
	 */
	public String[] getMatches() {
		List<String> matches = new ArrayList<String>();
		int offset = 0;

		while (matcher.find(offset)) {
			matches.add(TEXT.substring(matcher.start(), matcher.end()));
			offset = matcher.end();
		}

		return matches.toArray(new String[matches.size()]);
	}

	public static void main(String[] args) {
		RegexTester regTest = new RegexTester();

		System.out.println("Text:\t" + TEXT);
		System.out.println("Pattern:\t" + PATTERN);
		System.out.println("Matches:\t" + regTest.matches());
		System.out.println("All Matches:");
		for (String match : regTest.getMatches()) {
			System.out.println(match);
		}
	}

}
```

EDIT:
Wichtig ist, dass die Gruppen innerhalb der eckigen Klammern erhalten bleiben, da der darauf aufbauende Code später anhand dieser Gruppen den Tagnamen und den Parameter sucht.

mpG
Tobias


----------



## SlaterB (22. Jul 2008)

ich behaupte, die Frage schon beantwortet zu haben,

wenn dem nicht so ist, dann bitte Rückmeldung  :bae:


----------



## Tobias (22. Jul 2008)

siehe Edit 

Wie fasse ich die Zeichensequenz (inklusive der Match-Gruppen zwischen den eckigen Klammern so zusammen, das ich die Zeichengruppe [^\[\]] davon subtrahieren kann?

mpG
Tobias


----------



## SlaterB (22. Jul 2008)

mit meinem Pattern drin ist die Ausgabe

Text:	[h1]Überschrift[/h1][br /]Satz 1[br /]Satz 2[br /][imp]Wichtiger Satz 3[/imp]
Pattern:	(\[([^\[\]]*)/\])
Matches:	true
All Matches:
[br /]
[br /]
[br /]


also nur "Überschrift[/h1]" weg, wie gewünscht,

was ansonsten die Aufgabe ist und was 
[(.+?)(=(.+?))?\\s?
genau bedeutet habe ich noch nicht durchdrungen,

was genau läuft denn nun noch nicht?


----------



## SlaterB (22. Jul 2008)

vielleicht sowas:

```
public class RegexTester
{
    public static final String TEXT = "[h1]Überschrift[/h1][br x = y /]Satz 1[br /]Satz 2[br /][imp]Wichtiger Satz 3[/imp]";
    public static final String PATTERN = "(\\[([^\\[\\]=]+)?(=([^\\[\\]]+))?\\s?+/\\])";

    private Matcher matcher;

    public RegexTester()
    {
        matcher = Pattern.compile(PATTERN).matcher(TEXT);
    }

    public void getMatches()
    {
        while (matcher.find())
        {
            System.out.println("found: " + matcher.group(1) + ", " + matcher.group(2) + ", " + matcher.group(3));
        }
    }

    public static void main(String[] args)
    {
        RegexTester regTest = new RegexTester();

        System.out.println("Text:\t" + TEXT);
        System.out.println("Pattern:\t" + PATTERN);
        System.out.println("All Matches:");
        regTest.getMatches();
    }
}
```


----------



## Tobias (22. Jul 2008)

Ein Tag besteht aus zwei Teilen, der zweite ist optional:

1. Der Tag-Name, in unseren Fall "br".
2. Ein Parameter, welcher vom Tag-Namen durch ein "=" getrennt wird. Das ermöglicht zum Beispiel [br=wide /], um einen breiten Zeilenumbruch einzufügen (nur ein Beispiel).


```
(\[(.+?)(=(.+?))?\s?/\])
```

matcht Ausdrücke, die von einer führenden eckigen Klammer eingeleitet werden und durch Slash + schließende eckige Klammer beendet werden. Innerhalb der eckigen Klammern befindet sich dabei ein Ausdruck folgender Struktur:
Zuerst eine beliebige Zeichenkette aus mindestens einem Zeichen (matcht sowenig Zeichen wie möglich) (das ist der Tag-Name), gefolgt von einer optionalen Zeichenkette bestehend aus einem "=" mindestens einem Zeichen (reluctant) (das ist der optionale Parameter-String). Zwischen dem Parameter und der schließenden Slash+eckiger Klammer-Kombination kann beliebig viel Whitespace stehen.

Wichtig für die spätere Verarbeitung sind die Match-Gruppen des Ausdrucks (die runden Klammern):

1. Gruppe: Der gesamte Tag
2. Gruppe: Der Tag-Name
3. Gruppe: Der Parameter inklusive "="
4. Gruppe: Der Parameter

nummeriert werden die Gruppen nach der Reihenfolge ihrer öffnenden Klammern.

Diese innere Struktur des Regex muß bewahrt werden, gleichzeitig müssen aber eckige Klammern außer der öffnenden und der schließenden an den Enden verboten werden. Bei deinem Pattern ist weder sichergestellt, das der Tag die richtige Syntax befolgt (es würden auch Klammersätze zweiter Ordnung gematcht [also sowas hier] und müssten anschließend manuell ausgesondert werden), noch stehen die oben beschriebenen Match-Gruppen für die Weiterverarbeitung zur Verfügung.

mpG
Tobias


----------



## Tobias (22. Jul 2008)

Danke! Dein zweiter Vorschlag tut - glaube ich, Tests laufen gerade - genau was ich wollte. Ich hab nur das erste Fragezeichen in die Klammer geschoben, weil der Tag-Name nicht optional ist, sondern nur möglichst kurz. Vielen Dank für deine Hilfe!

mpG
Tobias


----------

