Teilen Sie einen Text in Sätze auf

Lesezeit: 2 Minuten

Wie kann ich einen Text in eine Reihe von Sätzen aufteilen?

Beispieltext:

Brat mir einen Biber. Brat mir einen Biber! Brat mir einen Biber? Brat mich Biber Nr. 4?! Brat mir viele Biber … Ende

Sollte ausgeben:

0 => Fry me a Beaver.
1 => Fry me a Beaver!
2 => Fry me a Beaver?
3 => Fry me Beaver no. 4?!
4 => Fry me many Beavers...
5 => End

Ich habe einige Lösungen ausprobiert, die ich über die Suche auf SO gefunden habe, aber alle schlagen fehl, insbesondere im 4. Satz.

/(?<=[!?.])./

/\.|\?|!/

/((?<=[a-z0-9)][.?!])|(?<=[a-z0-9][.?!]\"))(\s|\r\n)(?=\"?[A-Z])/

/(?<=[.!?]|[.!?][\'"])\s+/    // <- closest one

  • Der Satz #4 folgt nicht der Standard-Syntax. Sie brauchen eine Klasse von Terminators – Token, die das Ende eines Satzes markieren. Wenn Sie eines der Terminatoren als reguläres Symbol verwenden, ist es entweder kein Terminator oder Sie bilden die Sätze falsch. Sie können Ihren Kuchen nicht haben und ihn auch essen, um es einfach auszudrücken.

    – Hai

    4. Mai 2013 um 18:14 Uhr

  • Ich mache Kuchen und esse sie die ganze Zeit 😛 Kann eine Regex wie 2 Zeichen vorausschauen und wenn das 2. Zeichen kein Großbuchstabe AZ ist, bedeutet dies, dass die Interpunktion davor nicht gültig ist

    – thelolcat

    4. Mai 2013 um 18:16 Uhr


  • Klingt, als wüssten Sie bereits, was zu tun ist.

    – Hai

    4. Mai 2013 um 18:20 Uhr

  • Aber wie bekomme ich das in die Regex?

    – thelolcat

    4. Mai 2013 um 18:21 Uhr

  • @thelolcat Sie sind besser dran mit Ihrem eigenen Parser … eine einzelne Regex reicht nicht aus! Sie müssen Sätze berücksichtigen, die enthalten Mr.thelolcat, no.1

    – Anirudha

    4. Mai 2013 um 18:32 Uhr


Benutzer-Avatar
HamZa

Da Sie Sätze “aufteilen” möchten, warum versuchen Sie, sie abzugleichen?

Für diesen Fall verwenden wir preg_split().

Code:

$str="Fry me a Beaver. Fry me a Beaver! Fry me a Beaver? Fry me Beaver no. 4?! Fry me many Beavers... End";
$sentences = preg_split('/(?<=[.?!])\s+(?=[a-z])/i', $str);
print_r($sentences);

Ausgabe:

Array
(
    [0] => Fry me a Beaver.
    [1] => Fry me a Beaver!
    [2] => Fry me a Beaver?
    [3] => Fry me Beaver no. 4?!
    [4] => Fry me many Beavers...
    [5] => End
)

Erläuterung:

Nun, um es einfach auszudrücken, wir teilen uns auf gruppiert Leerzeichen \s+ und zwei Dinge tun:

  1. (?<=[.?!]) Positiver Blick hinter Behauptung, im Grunde suchen wir, ob hinter dem Leerzeichen ein Punkt oder ein Fragezeichen oder ein Ausrufezeichen steht.

  2. (?=[a-z]) Positive Look-Ahead-Assertion, Suche nach einem Buchstaben nach dem Leerzeichen, dies ist eine Art Problemumgehung für die no. 4 Problem.

  • Nur eine Frage: sollte nicht \s sein \s+ ? Ich meine, mehrere zusammen gruppierte Leerzeichen zu ignorieren

    – thelolcat

    4. Mai 2013 um 19:09 Uhr

  • Vielen Dank! Fügen Sie es meiner Hilfsbibliothek hinzu – github.com/Cosmologist/Gears/blob/master/src/Gears/StringType/…

    – Kosmologe

    6. Oktober 2016 um 13:50 Uhr

  • @ Ryan schnell (?<!\.\.\.)(?<=[.?!]|\.\))\s+(?=[a-z]). Sehen Sie, ob es Ihren Bedürfnissen entspricht.

    – HamZa

    11. Februar 2017 um 0:39 Uhr

  • Basierend auf dem, was ich von Ihnen gelernt habe, konnte ich es bearbeiten, um noch mehr Eckfälle zu behandeln, auf die ich stoße: regex101.com/r/e4NYyd/4 Cooles Zeug.

    – Ryan

    11. Februar 2017 um 1:39 Uhr

  • Das funktioniert nicht. Versuchen Sie, dem Satz “ie ” hinzuzufügen, diese Regex scheitert daran

    – Richard

    17. Mai 2018 um 19:42 Uhr

Ich empfehle, ohne Lookbehind nach Ihrem trennenden Satzzeichen zu suchen und dann diese übereinstimmenden Zeichen freizugeben (mit \K), passt dann das Leerzeichen an und sucht dann nach einem Großbuchstaben, der den Anfang des nächsten Satzes darstellt.

Code: (Demo)

$str="Fry me a Beaver. Fry me a Beaver! Fry me a Beaver? Fry me Beaver no. 4?! Fry me many Beavers... End";

var_export(
    preg_split('~[.?!]+\K\s+(?=[A-Z])~', $str, 0, PREG_SPLIT_NO_EMPTY)
);

Ausgabe:

array (
  0 => 'Fry me a Beaver.',
  1 => 'Fry me a Beaver!',
  2 => 'Fry me a Beaver?',
  3 => 'Fry me Beaver no. 4?!',
  4 => 'Fry me many Beavers...',
  5 => 'End',
)

Obwohl dies für die Beispielzeichenfolge nicht erforderlich ist, PREG_SPLIT_NO_EMPTY verhindert, dass am Ende des Arrays ein leeres Element erstellt wird, wenn die Zeichenfolge mit einem Satzzeichen endet.

Verwenden \K in meiner Antwort erfordert weniger Backtracking. Dadurch kann die Regex-Engine effizienter durch die Zeichenfolge “schreiten”. In Hamzas Antwort beginnt die Regex-Engine jedes Mal mit dem Abgleich, wenn ein Leerzeichen vorhanden ist. Nachdem das Leerzeichen abgeglichen wurde, muss sie rückwärts lesen, um nach der Interpunktion zu suchen. Wenn dies in Frage kommt, muss sie nach einem Buchstaben suchen.

Bei meinem Ansatz beginnt die Regex-Engine erst dann mit der Berücksichtigung von Übereinstimmungen, wenn sie auf eines der aufgelisteten Satzzeichen stößt, und sie blickt nie zurück. Es gibt viele passende Felder, aber viel weniger qualifizierende Symbole. Aus diesen Gründen wird in der Beispiel-Eingabezeichenfolge Mein Muster teilt die Saite in 40 Schritte und Hamzas Muster teilt die Saite in 74 Schritte.

Diese Effizienz ist es nicht wirklich wert, mit relativ kleinen Strings anzugeben, aber wenn Sie große Texte parsen, dann werden Effizienz und die Minimierung von Backtracking wichtiger.

1018440cookie-checkTeilen Sie einen Text in Sätze auf

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy