Wie teile ich Text in Sätze auf?
Verwenden Sie Intl.Segmenter, um Text mit lokalisierter Grenzerkennung in Sätze aufzuteilen, die Interpunktion, Abkürzungen und sprachspezifische Regeln berücksichtigt.
Einführung
Wenn Sie Text für Übersetzung, Analyse oder Anzeige verarbeiten, müssen Sie ihn oft in einzelne Sätze aufteilen. Ein naiver Ansatz mit regulären Ausdrücken scheitert, weil Satzgrenzen komplexer sind als Punkte gefolgt von Leerzeichen. Sätze können mit Fragezeichen, Ausrufezeichen oder Auslassungspunkten enden. Punkte erscheinen in Abkürzungen wie "Dr." oder "Inc.", ohne Sätze zu beenden. Verschiedene Sprachen verwenden unterschiedliche Satzzeichen als Satzabschluss.
Die Intl.Segmenter-API löst dieses Problem durch lokalisierte Erkennung von Satzgrenzen. Sie versteht die Regeln zur Identifizierung von Satzgrenzen in verschiedenen Sprachen und behandelt Sonderfälle wie Abkürzungen, Zahlen und komplexe Interpunktion automatisch.
Das Problem mit der Aufteilung an Punkten
Sie können versuchen, Text in Sätze aufzuteilen, indem Sie an Punkten gefolgt von Leerzeichen trennen.
const text = "Hello world. How are you? I am fine.";
const sentences = text.split(". ");
console.log(sentences);
// ["Hello world", "How are you? I am fine."]
Dieser Ansatz hat mehrere Probleme. Erstens behandelt er keine Fragezeichen oder Ausrufezeichen. Zweitens bricht er bei Abkürzungen, die Punkte enthalten. Drittens entfernt er den Punkt aus jedem Satz außer dem letzten. Viertens funktioniert er nicht, wenn mehrere Leerzeichen nach Punkten stehen.
const text = "Dr. Smith works at Acme Inc. He starts at 9 a.m.";
const sentences = text.split(". ");
console.log(sentences);
// ["Dr", "Smith works at Acme Inc", "He starts at 9 a.m."]
Der Text wird bei "Dr." und "Inc." falsch aufgeteilt, weil diese Abkürzungen Punkte enthalten. Sie benötigen einen intelligenteren Ansatz, der Satzgrenzregeln versteht.
Verwendung eines komplexeren regulären Ausdrucks
Sie können den regulären Ausdruck verbessern, um mehr Fälle zu behandeln.
const text = "Hello world. How are you? I am fine!";
const sentences = text.split(/[.?!]\s+/);
console.log(sentences);
// ["Hello world", "How are you", "I am fine", ""]
Dies teilt an Punkten, Fragezeichen und Ausrufezeichen gefolgt von Leerzeichen. Es behandelt mehr Fälle, scheitert aber immer noch bei Abkürzungen und erzeugt leere Zeichenketten. Es entfernt auch die Interpunktion aus jedem Satz.
const text = "Dr. Smith works at Acme Inc. He starts at 9 a.m.";
const sentences = text.split(/[.?!]\s+/);
console.log(sentences);
// ["Dr", "Smith works at Acme Inc", "He starts at 9 a", "m", ""]
Der Regex-Ansatz kann nicht zuverlässig zwischen Punkten unterscheiden, die Sätze beenden, und Punkten, die in Abkürzungen vorkommen. Der Aufbau eines umfassenden Regex, der alle Sonderfälle behandelt, wird unpraktikabel. Sie benötigen eine Lösung, die linguistische Regeln versteht.
Verwendung von Intl.Segmenter zur Satzaufteilung
Der Intl.Segmenter-Konstruktor erstellt einen Segmenter, der Text basierend auf gebietsschemaspezifischen Regeln aufteilt. Sie geben ein Gebietsschema an und setzen die granularity-Option auf "sentence".
const segmenter = new Intl.Segmenter("en", { granularity: "sentence" });
const text = "Hello world. How are you? I am fine!";
const segments = segmenter.segment(text);
for (const segment of segments) {
console.log(segment.segment);
}
// "Hello world. "
// "How are you? "
// "I am fine!"
Die segment()-Methode gibt ein Iterable zurück, das Segmentobjekte liefert. Jedes Segmentobjekt hat eine segment-Eigenschaft, die den Text dieses Segments enthält. Der Segmenter bewahrt die Interpunktion und Leerzeichen am Ende jedes Satzes.
Sie können die Segmente mit Array.from() in ein Array umwandeln.
const segmenter = new Intl.Segmenter("en", { granularity: "sentence" });
const text = "Hello world. How are you? I am fine!";
const segments = segmenter.segment(text);
const sentences = Array.from(segments, s => s.segment);
console.log(sentences);
// ["Hello world. ", "How are you? ", "I am fine!"]
Dies erstellt ein Array, in dem jedes Element ein Satz mit seiner ursprünglichen Interpunktion und Abstände ist.
Wie Intl.Segmenter Abkürzungen behandelt
Der Segmenter versteht gängige Abkürzungsmuster und teilt nicht bei Punkten, die innerhalb von Abkürzungen erscheinen.
const segmenter = new Intl.Segmenter("en", { granularity: "sentence" });
const text = "Dr. Smith works at Acme Inc. He starts at 9 a.m.";
const segments = segmenter.segment(text);
const sentences = Array.from(segments, s => s.segment);
console.log(sentences);
// ["Dr. Smith works at Acme Inc. ", "He starts at 9 a.m."]
Der Text wird korrekt in zwei Sätze aufgeteilt. Die Punkte in "Dr.", "Inc." und "a.m." lösen keine Satzumbrüche aus, da der Segmenter diese als Abkürzungen erkennt. Diese automatische Behandlung von Sonderfällen ist der Grund, warum Intl.Segmenter Regex-Ansätzen überlegen ist.
Leerzeichen aus Sätzen entfernen
Der Segmenter enthält nachfolgende Leerzeichen in jedem Satz. Sie können diese Leerzeichen bei Bedarf entfernen.
const segmenter = new Intl.Segmenter("en", { granularity: "sentence" });
const text = "Hello world. How are you? I am fine!";
const segments = segmenter.segment(text);
const sentences = Array.from(segments, s => s.segment.trim());
console.log(sentences);
// ["Hello world.", "How are you?", "I am fine!"]
Die trim()-Methode entfernt führende und nachfolgende Leerzeichen aus jedem Satz. Dies ist nützlich, wenn Sie saubere Satzgrenzen ohne zusätzliche Abstände benötigen.
Abrufen von Segment-Metadaten
Jedes Segment-Objekt enthält Metadaten über die Position des Segments im Originaltext.
const segmenter = new Intl.Segmenter("en", { granularity: "sentence" });
const text = "Hello world. How are you?";
const segments = segmenter.segment(text);
for (const segment of segments) {
console.log({
text: segment.segment,
index: segment.index,
input: segment.input
});
}
// { text: "Hello world. ", index: 0, input: "Hello world. How are you?" }
// { text: "How are you?", index: 13, input: "Hello world. How are you?" }
Die Eigenschaft index gibt an, wo das Segment im Originaltext beginnt. Die Eigenschaft input enthält den vollständigen Originaltext. Diese Metadaten sind nützlich, wenn Sie Satzpositionen verfolgen oder den Originaltext rekonstruieren müssen.
Aufteilen von Sätzen in verschiedenen Sprachen
Verschiedene Sprachen haben unterschiedliche Regeln für Satzgrenzen. Der Segmentierer passt sein Verhalten basierend auf der angegebenen Locale an.
Im Japanischen können Sätze mit einem vollbreiten Punkt 。 enden, der als Kuten bezeichnet wird.
const segmenter = new Intl.Segmenter("ja", { granularity: "sentence" });
const text = "私は猫です。名前はまだない。";
const segments = segmenter.segment(text);
const sentences = Array.from(segments, s => s.segment);
console.log(sentences);
// ["私は猫です。", "名前はまだない。"]
Der Text wird korrekt an den japanischen Satzabschlusszeichen aufgeteilt. Ein für Englisch konfigurierter Segmentierer würde diese Grenzen nicht korrekt erkennen.
Im Hindi können Sätze mit einem vertikalen Balken । enden, der als Purna Viram bezeichnet wird.
const segmenter = new Intl.Segmenter("hi", { granularity: "sentence" });
const text = "यह एक वाक्य है। यह दूसरा वाक्य है।";
const segments = segmenter.segment(text);
const sentences = Array.from(segments, s => s.segment);
console.log(sentences);
// ["यह एक वाक्य है। ", "यह दूसरा वाक्य है।"]
Der Segmentierer erkennt den Devanagari-Punkt als Satzgrenze. Dieses locale-bewusste Verhalten ist entscheidend für die internationalisierte Textverarbeitung.
Verwendung der korrekten Locale für mehrsprachigen Text
Wenn Sie Text verarbeiten, der mehrere Sprachen enthält, wählen Sie die Locale, die der Hauptsprache des Textes entspricht. Der Segmentierer verwendet die angegebene Locale, um zu bestimmen, welche Grenzregeln angewendet werden sollen.
const englishText = "Hello world. How are you?";
const japaneseText = "私は猫です。名前はまだない。";
const englishSegmenter = new Intl.Segmenter("en", { granularity: "sentence" });
const japaneseSegmenter = new Intl.Segmenter("ja", { granularity: "sentence" });
const englishSentences = Array.from(
englishSegmenter.segment(englishText),
s => s.segment
);
const japaneseSentences = Array.from(
japaneseSegmenter.segment(japaneseText),
s => s.segment
);
console.log(englishSentences);
// ["Hello world. ", "How are you?"]
console.log(japaneseSentences);
// ["私は猫です。", "名前はまだない。"]
Das Erstellen separater Segmentierer für jede Sprache gewährleistet eine korrekte Grenzerkennung. Wenn Sie Text verarbeiten, bei dem die Sprache unbekannt ist, können Sie eine generische Locale wie "en" als Fallback verwenden, obwohl dies die Genauigkeit für nicht-englischen Text verringert.
Umgang mit Text ohne Satzgrenzen
Wenn Text keine Satzabschlusszeichen enthält, gibt der Segmentierer den gesamten Text als einzelnes Segment zurück.
const segmenter = new Intl.Segmenter("en", { granularity: "sentence" });
const text = "Hello world";
const segments = segmenter.segment(text);
const sentences = Array.from(segments, s => s.segment);
console.log(sentences);
// ["Hello world"]
Dieses Verhalten ist korrekt, da der Text keine Satzgrenzen enthält. Der Segmentierer teilt Text, der einen einzelnen Satz bildet, nicht künstlich auf.
Umgang mit leeren Strings
Der Segmentierer behandelt leere Strings, indem er einen leeren Iterator zurückgibt.
const segmenter = new Intl.Segmenter("en", { granularity: "sentence" });
const text = "";
const segments = segmenter.segment(text);
const sentences = Array.from(segments, s => s.segment);
console.log(sentences);
// []
Dies erzeugt ein leeres Array, was das erwartete Ergebnis für leere Eingaben ist.
Wiederverwendung von Segmentierern für bessere Performance
Das Erstellen eines Segmentierers verursacht einen gewissen Overhead. Wenn Sie mehrere Texte mit demselben Locale und denselben Optionen segmentieren müssen, erstellen Sie den Segmentierer einmal und verwenden Sie ihn wieder.
const segmenter = new Intl.Segmenter("en", { granularity: "sentence" });
const texts = [
"First text. With two sentences.",
"Second text. With three sentences. And more.",
"Third text."
];
texts.forEach(text => {
const sentences = Array.from(segmenter.segment(text), s => s.segment);
console.log(sentences);
});
// ["First text. ", "With two sentences."]
// ["Second text. ", "With three sentences. ", "And more."]
// ["Third text."]
Die Wiederverwendung des Segmentierers ist effizienter als das Erstellen eines neuen für jeden Text.
Erstellen einer Funktion zum Zählen von Sätzen
Sie können den Segmentierer verwenden, um Sätze in einem Text zu zählen.
function countSentences(text, locale = "en") {
const segmenter = new Intl.Segmenter(locale, { granularity: "sentence" });
const segments = segmenter.segment(text);
return Array.from(segments).length;
}
console.log(countSentences("Hello world. How are you?"));
// 2
console.log(countSentences("Dr. Smith works at Acme Inc. He starts at 9 a.m."));
// 2
console.log(countSentences("Single sentence"));
// 1
console.log(countSentences("私は猫です。名前はまだない。", "ja"));
// 2
Diese Funktion erstellt einen Segmentierer, teilt den Text auf und gibt die Anzahl der Segmente zurück. Sie behandelt Abkürzungen und sprachspezifische Grenzen korrekt.
Erstellen einer Funktion zur Satzextraktion
Sie können eine Funktion erstellen, die einen bestimmten Satz aus einem Text anhand des Index extrahiert.
function getSentence(text, index, locale = "en") {
const segmenter = new Intl.Segmenter(locale, { granularity: "sentence" });
const segments = Array.from(segmenter.segment(text), s => s.segment);
return segments[index] || null;
}
const text = "First sentence. Second sentence. Third sentence.";
console.log(getSentence(text, 0));
// "First sentence. "
console.log(getSentence(text, 1));
// "Second sentence. "
console.log(getSentence(text, 2));
// "Third sentence."
console.log(getSentence(text, 3));
// null
Diese Funktion gibt den Satz am angegebenen Index zurück oder null, wenn der Index außerhalb der Grenzen liegt.
Überprüfung der Browser- und Runtime-Unterstützung
Die Intl.Segmenter-API ist in modernen Browsern und Node.js verfügbar. Sie wurde im April 2024 Teil der Web-Plattform-Baseline und wird in allen großen Browser-Engines unterstützt.
Sie können überprüfen, ob die API verfügbar ist, bevor Sie sie verwenden.
if (typeof Intl.Segmenter !== "undefined") {
const segmenter = new Intl.Segmenter("en", { granularity: "sentence" });
const text = "Hello world. How are you?";
const sentences = Array.from(segmenter.segment(text), s => s.segment);
console.log(sentences);
} else {
console.log("Intl.Segmenter is not supported");
}
Für Umgebungen ohne Unterstützung müssen Sie eine Fallback-Lösung bereitstellen. Ein einfacher Fallback verwendet eine grundlegende Regex-Aufteilung, verliert jedoch die Genauigkeit der locale-bewussten Segmentierung.
function splitSentences(text, locale = "en") {
if (typeof Intl.Segmenter !== "undefined") {
const segmenter = new Intl.Segmenter(locale, { granularity: "sentence" });
return Array.from(segmenter.segment(text), s => s.segment);
}
// Fallback for older environments
return text.split(/[.!?]\s+/).filter(s => s.length > 0);
}
console.log(splitSentences("Hello world. How are you?"));
// ["Hello world. ", "How are you?"]
Diese Funktion verwendet Intl.Segmenter, wenn verfügbar, und greift in älteren Umgebungen auf Regex-Splitting zurück. Der Fallback verliert Funktionen wie die Behandlung von Abkürzungen und sprachspezifische Grenzerkennung, bietet aber grundlegende Funktionalität.