# RegEx: String A im String B ersetzen mit modifiziertem String A



## N0ACE (22. August 2013)

Hallo liebe Community,

ich habe folgende Situation: Ein String:

```
Das Modell AB0FF2DE23 ist in Reparatur
```

Dabei soll das Modell ("AB0FF2D23") umgewandelt werden. Die Modell-Nr. besteht immer aus drei Blöcken, welche unterschiedlich aufgebaut sein können.

Ziel ist es, den String so umzuwandeln, dass er wie folgt erscheint:

```
Das Modell AB0 FF2 DE23 ist in Reparatur
```

Das RegEx-Pattern, welches das Modell ("AB0FF2D23") herausfiltert ist vorhanden (die drei Blöcke können unterschiedlich aufgebaut sein):

```
([A-Z]{2}\d|\d{3})([A-Z]{2}\d{1,2}|\d{3}|[A-Z]{3})(_)?([A-Z]\d{3}|[A-Z]{1,3}\d{2}|[A-Z]{3}\d|[A-Z]{4}|\d{3})(_2)?
```

Wie erreiche ich es, dass o.g. Satz so umgewandelt wird, dass zwischen den drei Blöcken je ein Leerzeichen eingesetzt wird ("AB0 FF2 DE23") und damit das orig. Modell ersetze?


Vielen Dank für Eure Hilfe und weiterhin einen schönen Tag!!

Nachtrag:

Ich arbeite in PHP und meine bisherigen Ansätze beziehen sich auf eine Kombination von preg_match() um die Modell-Nr. herauzufiltern und via implode() "schön" zu machen und diese dann via preg_replace() im Ursprungs-String zu ersetzen.

Gibt es hier eine elegantere, performantere Möglichkeit?


----------



## deepthroat (22. August 2013)

Hi.

Was soll den mit den _ und _2 Teilen passieren?

Grundsätzlich kannst du natürlich versuchen mit einem preg_replace auszukommen, falls du die _ und _2 weglassen willst?


```
preg_replace('/([A-Z]{2}\d|\d{3})([A-Z]{2}\d{1,2}|\d{3}|[A-Z]{3})(_)?([A-Z]\d{3}|[A-Z]{1,3}\d{2}|[A-Z]{3}\d|[A-Z]{4}|\d{3})(_2)?/', '\1 \2 \4', $text);
```
Test: http://www.myregextester.com/?r=de2e1e69


----------



## N0ACE (22. August 2013)

Der "_" zwischen Block 2 und 3 und das "_2" am Ende sind optional und tauchen (leiden) in manchen Modell-Nr. auf und sind daher notwendig. Sollte eines der beiden gefunden werden, soll da aber KEINE Leerzeichen zwischen.

Bsp:

```
AB0FF2_DE23_2
```

Soll zu:

```
AB0 FF2_DE23_2
```

Ist ein wenig fickerig, aber leider Voraussetzung ...


Danke für die Hilfe!

Dein Ansatz gefällt mir sehr gut! Wie gesagt, sind "_" und "_2" notwendig.

Gibt es eine Möglichkeit, das Leerzeichen zwischen Block 2 und 3 nur dann zu setzen, wenn KEIN "_" gefunden wurde?

Hier mein Bsp.: http://www.myregextester.com/?r=2fac15c6


----------



## Yaslaw (22. August 2013)

Alle Nummern finden die nicht an eine Zahl oder einen Buchstaben grenzen (dank den Assertions am Anfang und am Ende des Pattern)


```
== Findet Die Nummer ==
Das Modell AB0FF2DE23 ist in Reparatur
Das Modell AB0FF2DE23
Das Modell AB0FF2DE23.
Das Modell "AB0FF2DE23"
Das Modell (AB0_FF2 DE23)

==Findet Nicht
Das ModellAB0FF2DE23
Das Modell AB0FF2DE23123
```


```
$pattern = '/(?<![[:alnum:]])([[:alpha:]]{2}\d)[ ]?([[:alpha:]]{2}\d)[ ]?([[:alpha:]]{2}\d{2})(?![[:alnum:]])/';
$replace = '\1 \2 \3'; 
$result = preg_replace($pattern, $replace, $subject);
```

Und falls es anstelle von Leerzeichen _ haben kann

```
$pattern = '/(?<![[:alnum:]])([[:alpha:]]{2}\d)[ _]?([[:alpha:]]{2}\d)[ _]?([[:alpha:]]{2}\d{2})(?![[:alnum:]])/';
```


----------



## deepthroat (22. August 2013)

N0ACE hat gesagt.:


> Der "_" zwischen Block 2 und 3 und das "_2" am Ende sind optional und tauchen (leiden) in manchen Modell-Nr. auf und sind daher notwendig. Sollte eines der beiden gefunden werden, soll da aber KEINE Leerzeichen zwischen.
> 
> Bsp:
> 
> ...


Nein, das geht lieder nicht mit preg_replace. Den zweiten Fall kann man noch als Spezialfall behandeln indem man das _2 einfach in die Gruppe hineinzieht:

```
([A-Z]{2}\d|\d{3})([A-Z]{2}\d{1,2}|\d{3}|[A-Z]{3})(_?)((:?[A-Z]\d{3}|[A-Z]{1,3}\d{2}|[A-Z]{3}\d|[A-Z]{4}|\d{3})(:?_2)?)
```
Aber beim _ funktioniert das Leider nicht. Du kannst stattdessen preg_replace_callback Funktion verwenden oder mußt mehrfach ersetzen: 1. Fall ohne _ und mit Leerzeichen, 2. Fall mit _ und ohne Leerzeichen.


```
function format_model($matches) {
  $delim = ($matches[3]) ? $matches[3] : ' ';

  return $matches[1] . ' ' . $matches[2] . $delim . $matches[4];
}

preg_replace_callback('/([A-Z]{2}\d|\d{3})([A-Z]{2}\d{1,2}|\d{3}|[A-Z]{3})(_?)((:?[A-Z]\d{3}|[A-Z]{1,3}\d{2}|[A-Z]{3}\d|[A-Z]{4}|\d{3})(:?_2)?)/'',
  'format_model',
  '...');
```
(ungetestet)


----------



## N0ACE (23. August 2013)

Vielen Dank für die rasche, zahlreiche und *sehr gute* Hilfe!

Nach Absprache werden wir so verbleiben, dass wir den Underscore zwischen Block 2 und 3 nicht darstellen werden, sodass wir mit folgender Lösung äußerst zufrieden sind: http://www.myregextester.com/?r=c5e7f0c0

Nochmals vielen Dank******


----------

