# [RegEx] Zahlen und Dezimalzahlen finden



## ricounltd (28. September 2012)

Hallo,
ich sitze schon wieder vor einem Problem. Ich möchte unterschiedliche Produktnamen modifizieren, um genauer zu sein, ich möchte verschiedene Formate aus den Namen filtern.

Ich habe folgende Formate:
- 14,5x181x1800
- 15x189x1860
- 7x68x1072
- 15x195x2000-2400
- 15x195/220/250x2000-2400
- 15x161/195x1850/2000

Diese Formate kann es im Produktnamen geben und diese möchte ich gern filtern, nur kenne ich keine vernüftige Regexp dafür. Im Moment arbeite ich mit folgender Regex:

```
'/(\s[0-9]{1,3}x(.*)[0-9]{1,5}(\s|mm))|(\s[0-9]{1,2},[0-9]{1,2}x(.*)[0-9]{1,5}(\s|mm))/'
```

...um jetzt nur das Format zu erhalten ersetze, ich diese Pattern mit:

```
'$1'
```

Das funktioniert soweit auch ganz gut, bis auf Dezimalzahlen - da haut es mir alles durcheinander. Meine komplette Funktion sieht so aus:

```
preg_replace('/(\s[0-9]{1,3}x(.*)[0-9]{1,5}(\s|mm))|(\s[0-9]{1,2},[0-9]{1,2}x(.*)[0-9]{1,5}(\s|mm))/','Format: $1',$content);
```

Ich möchte einfach aus folgendem Beispiel (unabhängig vom Format):

```
Produktname und verschiedene Attribute 14,5x181x1800 Systemname
```

das Format, in folgender Form erhalten:

```
Format: 14,5x181x1800
```

...ich weiss nicht mehr weiter... kenne mich jetzt wirklich auch nicht so aus in diesem Bereich und hoffe deshalb auf hilfreiche Unterstützung. Vielen Dank!


----------



## timestamp (28. September 2012)

Eventuell so?

```
$string = "Produktname und verschiedene Attribute 14,5dm x 181cm x 1800mm Systemname";
$regex = "/([0-9]+(,[0-9]+)?(mm|m|cm|dm) ?x ?){2}([0-9]+(,[0-9]+)?)(mm|m|cm|dm)/i";
preg_match($regex, $string, $match);
echo 'Format: '.$match[0];
```


----------



## alxy (28. September 2012)

wie liegen den die Daten im Rohformat for? Einfach so als txt mit Leerzeichen als Trenner?


----------



## ricounltd (1. Oktober 2012)

Hey, danke dir. Deine Lösung funktioniert leider nicht. Das Array bleibt leer.

@alxy: Es sieht so aus, das ich die Daten aus dem <title>-Tag der Site bekomme.


----------



## timestamp (1. Oktober 2012)

Meine Lösung funktioniert, ich habe sie getestet.

Hier akzeptiert sie auch Punkte als Komma sowie ein Leerzeichen zwischen Zahl und Maßeinheit. 
Außerdem ist die Maßeinheit optional.

```
$pattern = "/([0-9]+([.,][0-9]+)? ?(mm|m|cm|dm)? ?x ?){2}([0-9]+([.,][0-9]+)?) ?(mm|m|cm|dm)?/";
$str = "Produktname und verschiedene Attribute 14,5x181x1800 Systemname";
preg_match($pattern, $string, $match); 
echo 'Format: '.$match[0];
```


----------



## RavenOnJ (1. Oktober 2012)

es fehlten die Delimiter im Pattern

```
$pattern = "/([0-9]+([.,][0-9]+)? ?(mm|m|cm|dm)? ?x ?){2}([0-9]+([.,][0-9]+)? ?(mm|m|cm|dm)? ?)/";
```

Gruß
Peter


----------



## timestamp (1. Oktober 2012)

Die waren im obigen funktionierenden Beispiel auch schon vorhanden. Hab sie im zweiten Post mal nachgetragen.


----------



## ricounltd (4. Oktober 2012)

Hey, ja ok - das Regex funktioniert, allerdings nur bei einem solchen Format "14,5x181x1800", also mit Kommastelle. Bei folgenden Formaten funktioniert es nicht: "15x160/195x2000-2400"


----------



## timestamp (4. Oktober 2012)

Allgemeiner wäre es dann so:

```
$regex = "/(\d[^x]*x[^x]*){2}(\d[^x]*x)?/";
$str = "15x160/195x2000-2400";
preg_match($regex, $str, $match);
echo "Maße: {$match[0]};";
```


----------



## Yaslaw (4. Oktober 2012)

Viel zu kompliziert.

```
/[ ]([^ ]+x[^ ]+x[^ ]+)[ ]/
```


----------



## RavenOnJ (4. Oktober 2012)

@rico

vielleicht schreibst du mal, welche Formate vorkommen können? Dein neues Format hattest du bisher nicht erwähnt. Ich nehme an, "160/195" soll heißen "160 oder 195" und  "2000-2400" steht für "2000 bis 2400". Können solche Formate in allen drei Positionen vorkommen, also z.B. 
"130/200x2000-2300x300/400" ?

@timestamp @Yaslaw
das soll funktionieren? Bei mir nicht

Gruß
Peter


----------



## Yaslaw (4. Oktober 2012)

Bei mir funktioniert es

```
<?PHP
  //
  // Text, der durchsucht werden soll
  //
  $subject = 'Produktname und verschiedene Attribute 15x160/195x2000-2400 Systemname';
  //
  // Der Reguläre Ausdruck
  //
  $pattern = '/[ ]([^ ]+x[^ ]+x[^ ]+)[ ]/';
  //
  // RegExp auswerten
  //
  $result = preg_match($pattern, $subject, $subpattern);
  //
  // Ergebnis ausgeben
  //
  echo '<p>Der verwendete Reguläre Ausdruck trifft auf den zu durchsuchenden Text '.
       (($result) ? '' : 'nicht ').'zu.</p>';
  //
  // Unterausdrücke ausgeben
  //
  echo '<pre>'.print_r($subpattern, TRUE).'</pre>';
?>
```


```
Der Reguläre Ausdruck trifft auf den zu durchsuchenden Text zu. 

Array
(
    [0] =>  15x160/195x2000-2400 
    [1] => 15x160/195x2000-2400
)
```

Hier mein Test
http://regexp-evaluator.de/evaluator/120a5de13c23e1735ecaeefde7e12f7e/#ergebnis

Nachtrag: Mit preg_replace musst du natürlich auch alles andere finden um es dann zu ignorieren. Also vorne und hinten noch je ein .* setzen

```
<?PHP
  //
  // Text, der durchsucht werden soll
  //
  $subject = 'Produktname und verschiedene Attribute 15x160/195x2000-2400 Systemname';
  //
  // Der Reguläre Ausdruck
  //
  $pattern = '/.*[ ]([^ ]+x[^ ]+x[^ ]+)[ ].*/';
  //
  // Ersatzstring
  //
  $replace = 'Format: \1';
  //
  // RegExp auswerten, Ersetzung durchführen
  //
  $result = preg_replace($pattern, $replace, $subject);
  //
  // Ergebnis ausgeben
  //
  echo '<p>Ergebnis: '.htmlspecialchars($result).'</p>';
?>
```


```
Format: 15x160/195x2000-2400
```


----------



## RavenOnJ (4. Oktober 2012)

@Yaslaw

dann gib mir mal nen Tipp, warum es bei mir nicht funktioniert.

Gruß
Peter


----------



## Yaslaw (4. Oktober 2012)

Hast du mein Nachtrag gelesen?


----------



## RavenOnJ (4. Oktober 2012)

@Yaslaw
OK, dann vertraust du aber drauf, dass der Satz, in dem das Pattern vorkommt, eine bestimmte Form hat und dass das Format korrekt ist, da du keine Validierung vornimmst. 


Wie wärs mit (ok, paranoid)

```
$subject = "Produktname und verschiedene Attribute 14,5x60/190x1800-2000 Systemname sowiexxxbla  usw";
```

Mit deinem Pattern:

```
<?PHP
  //
  // Text, der durchsucht werden soll
  //
  $subject = 'Produktname und verschiedene Attribute 14,5x160/190x1800-2000 Systemname sowiexxxbla  usw';
  //
  // Der Reguläre Ausdruck
  //
  $pattern = '#.*[ ]([^ ]+x[^ ]+x[^ ]+)[ ].*#';
  //
  // RegExp auswerten
  //
  $result = preg_match($pattern, $subject, $subpattern);
  //
  // Ergebnis ausgeben
  //
  echo '<p>Der verwendete Reguläre Ausdruck trifft auf den zu durchsuchenden Text '.
       (($result) ? '' : 'nicht ').'zu.</p>';
  //
  // Unterausdrücke ausgeben
  //
  echo '<pre>'.print_r($subpattern, TRUE).'</pre>';
?>
```


```
Ergebnis: Format: sowiexxxbla
```

http://regexp-evaluator.de/evaluator/eae23a5a1f796fa84c8eaaa745be81e8/#ergebnis

Will man gleichzeitig auf Korrektheit des Formats prüfen, wird's halt komplizierter, z.B.:

```
<?PHP
  //
  // Text, der durchsucht werden soll
  //
  $subject = 'Produktname und verschiedene Attribute 14,5x160/190x1800-2000 Systemname sowiexxxbla  usw';
  //
  // Der Reguläre Ausdruck
  //
  $pattern = '#.*[ ]((\d+([,.]\d+)*([/-]\d+([,.]\d+)*)?x){2}\d+([,.]\d+)*([/-]\d+([,.]\d+)*)?)[ ].*#';
  //
  // RegExp auswerten
  //
  $result = preg_match($pattern, $subject, $subpattern);
  //
  // Ergebnis ausgeben
  //
  echo '<p>Der verwendete Reguläre Ausdruck trifft auf den zu durchsuchenden Text '.
       (($result) ? '' : 'nicht ').'zu.</p>';
  //
  // Unterausdrücke ausgeben
  //
  echo '<pre>'.print_r($subpattern, TRUE).'</pre>';
?>
```

http://regexp-evaluator.de/evaluator/65d614c00824440742e11bb20f29cd02/#ergebnis


```
Ergebnis: 14,5x160/190x1800-2000
```
Gruß
Peter


----------



## timestamp (4. Oktober 2012)

Letztendlich helfen so vage Formulierungen mit nachträglichen Pattern-Vorgaben nciht unbedingt das Problem optimal zu lösen (Optimieren bei RegExp ist sowieso immer so eine Sache).
Ich kann dem Threadersteller nur folgenden Link nahelegen:
Tutorial zu regulären Ausdrücken


----------



## Yaslaw (4. Oktober 2012)

Du musst wissen was daherkommen kann und wie paranoid die filtern musst. Ich ging von deinen Beispielen aus. Wenn du natürlich mit jedem deiner Postings einen neuen Sonderfall bringst können wir uns noch ewig im Kreise drehen.


----------



## RavenOnJ (4. Oktober 2012)

@Yaslaw
also, wenn ich ein Format prüfe, dann validiere ich auch. Dein Pattern lässt viel zu großen Freiraum. Wenn man paranoide Strings wie in meinem Beispiel ausschließen kann, dann reicht deine Lösung wohl. Aber das ist eine Frage der Herangehensweise.

Gruß
Peter


----------



## Yaslaw (4. Oktober 2012)

Vor allem der Anforderungen. Und diese kenne ich nicht.
Ich gebe ja auch zu dass dein letztes Beispiel mit meinem Pattern durchrasselt. Kein Problem, dann macht man ein schärferes Pattern. Die Frage ist nur, wie detailiert muss das Pattern sein. Und das beantwortet die Anforderungen.

Kommen diese Texte aus einem in sich geschlossenen System und man weiss was einem in etwa erwartet? Oder kann dieser Text von Kreti und Pleti erstellt werden?

Wenn mein Pattern zu lasch ist, dann kommen ggf mehr Werte durch als erwünscht.
Wenn mein Pattern zu strikt ist und die Formatvarianten nicht zu 100% bekannt sind, dann filtere ich ggf auch Werte aus die ich eigentlich haben möchte.

Den richtigen Schnitt, den musst du selber finden.


----------



## RavenOnJ (4. Oktober 2012)

@Yaslaw
Ich war nicht der Fragesteller, verwechsele das bitte nicht. Ansonsten stimmen wir überein.

Gruß
Peter


----------



## RavenOnJ (4. Oktober 2012)

das Pattern war immer noch nicht vollständig, da so was wie "120x160/180/190x120/130/200" durchfiel. Neues Pattern

```
$num_pat = "\d+([,.]\d+)*";
$range_pat = $num_pat . "((-" . $num_pat . ")?|(/". $num_pat . ")*)";
$pattern = "#.*[ ]((" . $range_pat . "x){2}" . $range_pat . ")[ ].*#";
```

was zu folgendem führt

```
$pattern = "#.*[ ]((\d+([,.]\d+)*((-\d+([,.]\d+)*)?|(/\d+([,.]\d+)*)*)x){2}\d+([,.]\d+)*((-\d+([,.]\d+)*)?|(/\d+([,.]\d+)*)*))[ ].*#";
```

Aber jetzt ist's genug.

Gruß
Peter


----------



## timestamp (4. Oktober 2012)

Ne, das wäre wiederum falsch.
Das num_pattern sollte wenn schon so aussehen:

```
$num_pat = "\d+([,.]\d+)?";
```


----------



## RavenOnJ (4. Oktober 2012)

ja, sorry, hast recht. Es geht natürlich nur 0 oder 1.

Gruß
Peter


----------

