Wie teile ich Text in Wörter in JavaScript auf?
Verwenden Sie Intl.Segmenter, um Wörter aus Text in jeder Sprache zu extrahieren, einschließlich Sprachen ohne Leerzeichen zwischen Wörtern.
Einführung
Wenn Sie Wörter aus einem Text extrahieren müssen, ist der übliche Ansatz, den Text mit split(" ") an Leerzeichen zu teilen. Dies funktioniert für Englisch, versagt jedoch vollständig bei Sprachen, die keine Leerzeichen zwischen Wörtern verwenden. Chinesisch, Japanisch, Thai und andere Sprachen schreiben Text kontinuierlich ohne Worttrennzeichen, dennoch nehmen Benutzer in diesem Text eindeutige Wörter wahr.
Die Intl.Segmenter-API löst dieses Problem. Sie identifiziert Wortgrenzen gemäß Unicode-Standards und linguistischen Regeln für jede Sprache. Sie können Wörter aus Text extrahieren, unabhängig davon, ob die Sprache Leerzeichen verwendet, und der Segmenter übernimmt die Komplexität der Bestimmung, wo Wörter beginnen und enden.
Dieser Artikel erklärt, warum einfache String-Teilung für internationalen Text fehlschlägt, wie Wortgrenzen in verschiedenen Schriftsystemen funktionieren und wie man Intl.Segmenter verwendet, um Text korrekt in Wörter für alle Sprachen zu teilen.
Warum das Teilen an Leerzeichen fehlschlägt
Die split()-Methode teilt einen String an jedem Vorkommen eines Trennzeichens. Für englischen Text extrahiert das Teilen an Leerzeichen Wörter.
const text = "Hello world";
const words = text.split(" ");
console.log(words);
// ["Hello", "world"]
Dieser Ansatz setzt voraus, dass Wörter durch Leerzeichen getrennt sind. Viele Sprachen folgen diesem Muster nicht.
Chinesischer Text enthält keine Leerzeichen zwischen Wörtern.
const text = "你好世界";
const words = text.split(" ");
console.log(words);
// ["你好世界"]
Der Benutzer sieht zwei verschiedene Wörter, aber split() gibt den gesamten String als einzelnes Element zurück, da keine Leerzeichen zum Teilen vorhanden sind.
Japanischer Text mischt mehrere Schriften und verwendet keine Leerzeichen zwischen Wörtern.
const text = "今日は良い天気です";
const words = text.split(" ");
console.log(words);
// ["今日は良い天気です"]
Dieser Satz enthält mehrere Wörter, aber das Teilen an Leerzeichen erzeugt ein Element.
Auch thailändischer Text schreibt Wörter kontinuierlich ohne Leerzeichen.
const text = "สวัสดีครับ";
const words = text.split(" ");
console.log(words);
// ["สวัสดีครับ"]
Der Text enthält zwei Wörter, aber split() gibt ein Element zurück.
Für diese Sprachen benötigen Sie einen anderen Ansatz, um Wortgrenzen zu identifizieren.
Warum reguläre Ausdrücke bei Wortgrenzen versagen
Wortgrenzen in regulären Ausdrücken verwenden das Muster \b, um Positionen zwischen Wort- und Nicht-Wort-Zeichen zu erkennen. Dies funktioniert für Englisch.
const text = "Hello world!";
const words = text.match(/\b\w+\b/g);
console.log(words);
// ["Hello", "world"]
Dieses Muster versagt bei Sprachen ohne Leerzeichen, da die Regex-Engine keine Wortgrenzen in Schriften wie Chinesisch, Japanisch oder Thai erkennt.
const text = "你好世界";
const words = text.match(/\b\w+\b/g);
console.log(words);
// ["你好世界"]
Die Regex behandelt die gesamte Zeichenfolge als ein Wort, da sie chinesische Wortgrenzen nicht versteht.
Selbst für Englisch können Regex-Muster bei Interpunktion, Kontraktionen oder Sonderzeichen falsche Ergebnisse liefern. Reguläre Ausdrücke sind nicht dafür konzipiert, linguistische Wortsegmentierung über alle Schriftsysteme hinweg zu bewältigen.
Was Wortgrenzen in verschiedenen Sprachen sind
Eine Wortgrenze ist eine Position im Text, an der ein Wort endet und ein anderes beginnt. Verschiedene Schriftsysteme verwenden unterschiedliche Konventionen für Wortgrenzen.
Leerzeichengetrennte Sprachen wie Englisch, Spanisch, Französisch und Deutsch verwenden Leerzeichen, um Wortgrenzen zu markieren. Das Wort "hello" wird durch ein Leerzeichen von "world" getrennt.
Scriptio-continua-Sprachen wie Chinesisch, Japanisch und Thai verwenden keine Leerzeichen zwischen Wörtern. Wortgrenzen existieren basierend auf semantischen und morphologischen Regeln, aber diese Grenzen sind im Text nicht visuell markiert. Ein chinesischer Leser erkennt, wo ein Wort endet und ein anderes beginnt, durch Vertrautheit mit der Sprache, nicht durch visuelle Trennzeichen.
Einige Sprachen verwenden gemischte Konventionen. Japanisch kombiniert Kanji-, Hiragana- und Katakana-Zeichen, und Wortgrenzen treten an Übergängen zwischen Zeichentypen oder basierend auf grammatikalischer Struktur auf.
Der Unicode-Standard definiert Wortgrenzenregeln in UAX 29. Diese Regeln spezifizieren, wie Wortgrenzen für alle Schriften identifiziert werden können. Die Regeln berücksichtigen Zeicheneigenschaften, Schrifttypen und linguistische Muster, um zu bestimmen, wo Wörter beginnen und enden.
Verwendung von Intl.Segmenter zur Aufteilung von Text in Wörter
Der Konstruktor Intl.Segmenter erstellt ein Segmenter-Objekt, das Text nach Unicode-Regeln aufteilt. Sie geben eine Locale und eine Granularität an.
const segmenter = new Intl.Segmenter("en", { granularity: "word" });
const text = "Hello world!";
const segments = segmenter.segment(text);
Das erste Argument ist der Locale-Bezeichner. Das zweite Argument ist ein Options-Objekt, wobei granularity: "word" dem Segmenter mitteilt, an Wortgrenzen zu teilen.
Die Methode segment() gibt ein iterierbares Objekt zurück, das Segmente enthält. Sie können mit for...of über die Segmente iterieren.
const segmenter = new Intl.Segmenter("en", { granularity: "word" });
const text = "Hello world!";
for (const segment of segmenter.segment(text)) {
console.log(segment);
}
// { segment: "Hello", index: 0, input: "Hello world!", isWordLike: true }
// { segment: " ", index: 5, input: "Hello world!", isWordLike: false }
// { segment: "world", index: 6, input: "Hello world!", isWordLike: true }
// { segment: "!", index: 11, input: "Hello world!", isWordLike: false }
Jedes Segment-Objekt enthält folgende Eigenschaften:
segment: der Text dieses Segmentsindex: die Position im ursprünglichen String, an der dieses Segment beginntinput: der ursprüngliche String, der segmentiert wirdisWordLike: ob dieses Segment ein Wort oder kein Wortinhalt ist
Verständnis der Eigenschaft isWordLike
Wenn Sie Text nach Wörtern segmentieren, gibt der Segmenter sowohl Wortsegmente als auch Nicht-Wortsegmente zurück. Wörter umfassen Buchstaben, Zahlen und ideografische Zeichen. Nicht-Wortsegmente umfassen Leerzeichen, Interpunktion und andere Trennzeichen.
Die Eigenschaft isWordLike gibt an, ob ein Segment ein Wort ist. Diese Eigenschaft ist true für Segmente, die Wortzeichen enthalten, und false für Segmente, die nur Leerzeichen, Interpunktion oder andere Nicht-Wortzeichen enthalten.
const segmenter = new Intl.Segmenter("en", { granularity: "word" });
const text = "Hello, world!";
for (const { segment, isWordLike } of segmenter.segment(text)) {
console.log(segment, isWordLike);
}
// "Hello" true
// "," false
// " " false
// "world" true
// "!" false
Verwenden Sie die Eigenschaft isWordLike, um Wortsegmente von Interpunktion und Leerzeichen zu filtern. Dies gibt Ihnen nur die Wörter ohne Trennzeichen.
const segmenter = new Intl.Segmenter("en", { granularity: "word" });
const text = "Hello, world!";
const segments = segmenter.segment(text);
const words = Array.from(segments)
.filter(s => s.isWordLike)
.map(s => s.segment);
console.log(words);
// ["Hello", "world"]
Dieses Muster funktioniert für jede Sprache, einschließlich solcher ohne Leerzeichen.
Extrahieren von Wörtern aus Text ohne Leerzeichen
Der Segmenter identifiziert korrekt Wortgrenzen in Sprachen, die keine Leerzeichen verwenden. Bei chinesischem Text teilt der Segmenter an Wortgrenzen basierend auf Unicode-Regeln und linguistischen Mustern.
const segmenter = new Intl.Segmenter("zh", { granularity: "word" });
const text = "你好世界";
for (const { segment, isWordLike } of segmenter.segment(text)) {
console.log(segment, isWordLike);
}
// "你好" true
// "世界" true
Der Segmenter identifiziert zwei Wörter in diesem Text. Es gibt keine Leerzeichen, aber der Segmenter versteht chinesische Wortgrenzen und teilt den Text entsprechend auf.
Bei japanischem Text bewältigt der Segmenter die Komplexität gemischter Schriften und identifiziert Wortgrenzen.
const segmenter = new Intl.Segmenter("ja", { granularity: "word" });
const text = "今日は良い天気です";
for (const { segment, isWordLike } of segmenter.segment(text)) {
console.log(segment, isWordLike);
}
// "今日" true
// "は" true
// "良い" true
// "天気" true
// "です" true
Der Segmenter teilt diesen Satz in fünf Wortsegmente auf. Er erkennt, dass Partikel wie "は" separate Wörter sind und dass zusammengesetzte Wörter wie "天気" einzelne Einheiten bilden.
Bei thailändischem Text identifiziert der Segmenter Wortgrenzen ohne Leerzeichen.
const segmenter = new Intl.Segmenter("th", { granularity: "word" });
const text = "สวัสดีครับ";
for (const { segment, isWordLike } of segmenter.segment(text)) {
console.log(segment, isWordLike);
}
// "สวัสดี" true
// "ครับ" true
Der Segmenter identifiziert korrekt zwei Wörter in dieser Begrüßung.
Erstellen einer Funktion zur Wortextraktion
Erstellen Sie eine Funktion, die Wörter aus Text in jeder Sprache extrahiert.
function getWords(text, locale) {
const segmenter = new Intl.Segmenter(locale, { granularity: "word" });
const segments = segmenter.segment(text);
return Array.from(segments)
.filter(s => s.isWordLike)
.map(s => s.segment);
}
Diese Funktion funktioniert sowohl für Sprachen mit Leerzeichen als auch für Sprachen ohne Leerzeichen.
getWords("Hello, world!", "en");
// ["Hello", "world"]
getWords("你好世界", "zh");
// ["你好", "世界"]
getWords("今日は良い天気です", "ja");
// ["今日", "は", "良い", "天気", "です"]
getWords("Bonjour le monde!", "fr");
// ["Bonjour", "le", "monde"]
getWords("สวัสดีครับ", "th");
// ["สวัสดี", "ครับ"]
Die Funktion gibt ein Array von Wörtern zurück, unabhängig von der Sprache oder dem Schriftsystem.
Wörter in verschiedenen Sprachen präzise zählen
Erstellen Sie einen Wortzähler, der für alle Sprachen funktioniert, indem wortähnliche Segmente gezählt werden.
function countWords(text, locale) {
const segmenter = new Intl.Segmenter(locale, { granularity: "word" });
const segments = segmenter.segment(text);
return Array.from(segments).filter(s => s.isWordLike).length;
}
Diese Funktion erzeugt genaue Wortzählungen für Text in jeder Sprache.
countWords("Hello world", "en");
// 2
countWords("你好世界", "zh");
// 2
countWords("今日は良い天気です", "ja");
// 5
countWords("Bonjour le monde", "fr");
// 3
countWords("สวัสดีครับ", "th");
// 2
Die Zählungen entsprechen der Wahrnehmung von Wortgrenzen durch Benutzer in jeder Sprache.
Herausfinden, welches Wort eine Position enthält
Die Methode containing() findet das Segment, das einen bestimmten Index im String enthält. Dies ist nützlich, um zu bestimmen, in welchem Wort sich der Cursor befindet oder welches Wort angeklickt wurde.
const segmenter = new Intl.Segmenter("en", { granularity: "word" });
const text = "Hello world";
const segments = segmenter.segment(text);
const segment = segments.containing(7);
console.log(segment);
// { segment: "world", index: 6, input: "Hello world", isWordLike: true }
Index 7 fällt in das Wort "world", das bei Index 6 beginnt. Die Methode gibt das Segmentobjekt für dieses Wort zurück.
Wenn der Index innerhalb eines Leerzeichens oder einer Interpunktion liegt, gibt die Methode dieses Segment mit isWordLike: false zurück.
const segment = segments.containing(5);
console.log(segment);
// { segment: " ", index: 5, input: "Hello world", isWordLike: false }
Verwenden Sie dies für Texteditor-Funktionen wie Doppelklick-Wortauswahl, kontextbezogene Menüs basierend auf der Cursorposition oder Hervorhebung des aktuellen Wortes.
Umgang mit Interpunktion und Kontraktionen
Der Segmenter behandelt Interpunktion als separate Segmente. Kontraktionen im Englischen werden typischerweise in mehrere Segmente aufgeteilt.
const segmenter = new Intl.Segmenter("en", { granularity: "word" });
const text = "I can't do it.";
for (const { segment, isWordLike } of segmenter.segment(text)) {
console.log(segment, isWordLike);
}
// "I" true
// " " false
// "can" true
// "'" false
// "t" true
// " " false
// "do" true
// " " false
// "it" true
// "." false
Die Kontraktion "can't" wird in "can", "'" und "t" aufgeteilt. Wenn Sie Kontraktionen als einzelne Wörter beibehalten müssen, benötigen Sie zusätzliche Logik, um Segmente basierend auf Apostrophen zusammenzuführen.
Für die meisten Anwendungsfälle liefert das Zählen wortähnlicher Segmente aussagekräftige Wortzählungen, auch wenn Kontraktionen aufgeteilt werden.
Wie sich die Locale auf die Wortsegmentierung auswirkt
Die Locale, die Sie an den Segmenter übergeben, beeinflusst, wie Wortgrenzen bestimmt werden. Verschiedene Locales können unterschiedliche Regeln für denselben Text haben.
Für Sprachen mit klar definierten Wortgrenzenregeln stellt die Locale sicher, dass die korrekten Regeln angewendet werden.
const segmenterEn = new Intl.Segmenter("en", { granularity: "word" });
const segmenterZh = new Intl.Segmenter("zh", { granularity: "word" });
const text = "你好世界";
const wordsEn = Array.from(segmenterEn.segment(text))
.filter(s => s.isWordLike)
.map(s => s.segment);
const wordsZh = Array.from(segmenterZh.segment(text))
.filter(s => s.isWordLike)
.map(s => s.segment);
console.log(wordsEn);
// ["你好世界"]
console.log(wordsZh);
// ["你好", "世界"]
Die englische Locale erkennt keine chinesischen Wortgrenzen und behandelt die gesamte Zeichenfolge als ein Wort. Die chinesische Locale wendet chinesische Wortgrenzenregeln an und identifiziert korrekt zwei Wörter.
Verwenden Sie immer die passende Locale für die Sprache des zu segmentierenden Textes.
Erstellung wiederverwendbarer Segmenter für bessere Performance
Die Erstellung eines Segmenters ist nicht ressourcenintensiv, aber Sie können Segmenter für mehrere Zeichenfolgen wiederverwenden, um die Performance zu verbessern.
const enSegmenter = new Intl.Segmenter("en", { granularity: "word" });
const zhSegmenter = new Intl.Segmenter("zh", { granularity: "word" });
const jaSegmenter = new Intl.Segmenter("ja", { granularity: "word" });
function getWords(text, locale) {
const segmenter = locale === "zh" ? zhSegmenter
: locale === "ja" ? jaSegmenter
: enSegmenter;
return Array.from(segmenter.segment(text))
.filter(s => s.isWordLike)
.map(s => s.segment);
}
Dieser Ansatz erstellt Segmenter einmalig und verwendet sie für alle Aufrufe von getWords() wieder. Der Segmenter speichert Locale-Daten im Cache, sodass durch die Wiederverwendung von Instanzen wiederholte Initialisierungen vermieden werden.
Praktisches Beispiel: Entwicklung eines Worthäufigkeitsanalysators
Kombinieren Sie Wortsegmentierung mit Zählung, um die Worthäufigkeit in Texten zu analysieren.
function getWordFrequency(text, locale) {
const segmenter = new Intl.Segmenter(locale, { granularity: "word" });
const segments = segmenter.segment(text);
const words = Array.from(segments)
.filter(s => s.isWordLike)
.map(s => s.segment.toLowerCase());
const frequency = {};
for (const word of words) {
frequency[word] = (frequency[word] || 0) + 1;
}
return frequency;
}
const text = "Hello world! Hello everyone in this world.";
const frequency = getWordFrequency(text, "en");
console.log(frequency);
// { hello: 2, world: 2, everyone: 1, in: 1, this: 1 }
Diese Funktion teilt Text in Wörter auf, normalisiert zu Kleinbuchstaben und zählt die Vorkommen. Sie funktioniert für jede Sprache.
const textZh = "你好世界!你好大家!";
const frequencyZh = getWordFrequency(textZh, "zh");
console.log(frequencyZh);
// { "你好": 2, "世界": 1, "大家": 1 }
Die gleiche Logik verarbeitet chinesischen Text ohne Modifikation.
Überprüfung der Browser-Unterstützung
Die Intl.Segmenter-API hat im April 2024 den Baseline-Status erreicht. Sie funktioniert in aktuellen Versionen von Chrome, Firefox, Safari und Edge. Ältere Browser unterstützen sie nicht.
Überprüfen Sie die Unterstützung, bevor Sie die API verwenden.
if (typeof Intl.Segmenter !== "undefined") {
const segmenter = new Intl.Segmenter("en", { granularity: "word" });
// Segmenter verwenden
} else {
// Fallback für ältere Browser
}
Für Produktionsanwendungen, die auf ältere Browser abzielen, stellen Sie eine Fallback-Implementierung bereit. Ein einfacher Fallback verwendet split() für englischen Text und gibt den gesamten String für andere Sprachen zurück.
function getWords(text, locale) {
if (typeof Intl.Segmenter !== "undefined") {
const segmenter = new Intl.Segmenter(locale, { granularity: "word" });
return Array.from(segmenter.segment(text))
.filter(s => s.isWordLike)
.map(s => s.segment);
}
// Fallback: funktioniert nur für Sprachen mit Leerzeichen-Trennung
return text.split(/\s+/).filter(word => word.length > 0);
}
Dies stellt sicher, dass Ihr Code in älteren Browsern läuft, wenn auch mit reduzierter Funktionalität für Sprachen ohne Leerzeichen-Trennung.
Häufige Fehler, die vermieden werden sollten
Teilen Sie mehrsprachigen Text nicht anhand von Leerzeichen oder Regex-Mustern auf. Diese Ansätze funktionieren nur für eine Teilmenge von Sprachen und versagen bei Chinesisch, Japanisch, Thai und anderen Sprachen ohne Leerzeichen.
Vergessen Sie nicht, bei der Extraktion von Wörtern nach isWordLike zu filtern. Ohne diesen Filter erhalten Sie Leerzeichen, Interpunktion und andere Nicht-Wort-Segmente in Ihren Ergebnissen.
Verwenden Sie nicht die falsche Locale bei der Segmentierung von Text. Die Locale bestimmt, welche Wortgrenzenregeln angewendet werden. Die Verwendung einer englischen Locale für chinesischen Text führt zu falschen Ergebnissen.
Nehmen Sie nicht an, dass alle Sprachen Wörter auf die gleiche Weise definieren. Wortgrenzen variieren je nach Schriftsystem und linguistischer Konvention. Verwenden Sie locale-bewusste Segmentierung, um diese Unterschiede zu berücksichtigen.
Zählen Sie Wörter nicht mit split(" ").length für internationalen Text. Dies funktioniert nur für Sprachen mit Leerzeichen-Trennung und liefert falsche Zählungen für andere.
Wann Wortsegmentierung eingesetzt werden sollte
Verwenden Sie Wortsegmentierung, wenn Sie:
- Wörter in benutzergenerierten Inhalten über mehrere Sprachen hinweg zählen müssen
- Such- und Hervorhebungsfunktionen implementieren möchten, die mit jedem Schriftsystem funktionieren
- Textanalysetools erstellen, die internationalen Text verarbeiten
- Wortbasierte Navigations- oder Bearbeitungsfunktionen in Texteditoren erstellen
- Schlüsselwörter oder Begriffe aus mehrsprachigen Dokumenten extrahieren
- Wortanzahlbegrenzungen in Formularen validieren, die jede Sprache akzeptieren
Verwenden Sie keine Wortsegmentierung, wenn Sie nur Zeichenanzahlen benötigen. Verwenden Sie für Operationen auf Zeichenebene die Graphemsegmentierung.
Verwenden Sie keine Wortsegmentierung für die Satztrennung. Verwenden Sie dafür die Satzgranularität.
Wie Wortsegmentierung in die Internationalisierung passt
Die Intl.Segmenter-API ist Teil der ECMAScript-Internationalisierungs-API. Andere APIs dieser Familie behandeln verschiedene Aspekte der Internationalisierung:
Intl.DateTimeFormat: Formatiert Datum und Uhrzeit entsprechend der LocaleIntl.NumberFormat: Formatiert Zahlen, Währungen und Einheiten entsprechend der LocaleIntl.Collator: Sortiert und vergleicht Strings entsprechend der LocaleIntl.PluralRules: Bestimmt Pluralformen für Zahlen in verschiedenen Sprachen
Zusammen bieten diese APIs die notwendigen Werkzeuge, um Anwendungen zu entwickeln, die für Benutzer weltweit korrekt funktionieren. Verwenden Sie Intl.Segmenter mit Wortgranularität, wenn Sie Wortgrenzen identifizieren müssen, und nutzen Sie die anderen Intl-APIs für Formatierung und Vergleich.