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 Text extrahieren müssen, besteht der gängige Ansatz darin, bei Leerzeichen mit split(" ") zu trennen. 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 fortlaufend ohne Worttrennzeichen, dennoch nehmen Benutzer deutliche Wörter in diesem Text 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 einfaches String-Splitting bei internationalem Text versagt, wie Wortgrenzen in verschiedenen Schriftsystemen funktionieren und wie Sie Intl.Segmenter verwenden, um Text für alle Sprachen korrekt in Wörter aufzuteilen.
Warum das Aufteilen bei Leerzeichen versagt
Die split()-Methode trennt einen String bei jedem Vorkommen eines Trennzeichens. Für englischen Text extrahiert das Aufteilen bei 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 deutliche Wörter, aber split() gibt den gesamten String als einzelnes Element zurück, da keine Leerzeichen zum Aufteilen vorhanden sind.
Japanischer Text mischt mehrere Schriftsysteme und verwendet keine Leerzeichen zwischen Wörtern.
const text = "今日は良い天気です";
const words = text.split(" ");
console.log(words);
// ["今日は良い天気です"]
Dieser Satz enthält mehrere Wörter, aber die Aufteilung nach Leerzeichen erzeugt ein Element.
Thailändischer Text schreibt Wörter ebenfalls 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 \b-Muster, um Positionen zwischen Wort- und Nicht-Wort-Zeichen zu finden. 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 Schriftsystemen wie Chinesisch, Japanisch oder Thailändisch erkennt.
const text = "你好世界";
const words = text.match(/\b\w+\b/g);
console.log(words);
// ["你好世界"]
Der reguläre Ausdruck behandelt den gesamten String als ein Wort, da er 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 für die linguistische Wortsegmentierung über alle Schriftsysteme hinweg konzipiert.
Was Wortgrenzen sprachübergreifend 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.
Durch Leerzeichen getrennte Sprachen wie Englisch, Spanisch, Französisch und Deutsch verwenden Leerzeichen zur Markierung von Wortgrenzen. Das Wort "hello" wird durch ein Leerzeichen von "world" getrennt.
Scriptio-continua-Sprachen wie Chinesisch, Japanisch und Thailändisch 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 durch Vertrautheit mit der Sprache, wo ein Wort endet und ein anderes beginnt, 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 der grammatikalischen Struktur auf.
Der Unicode-Standard definiert Wortgrenzregeln in UAX 29. Diese Regeln spezifizieren, wie Wortgrenzen für alle Schriftsysteme identifiziert werden. Die Regeln berücksichtigen Zeicheneigenschaften, Schrifttypen und linguistische Muster, um zu bestimmen, wo Wörter beginnen und enden.
Verwendung von Intl.Segmenter zum Aufteilen von Text in Wörter
Der Intl.Segmenter-Konstruktor erstellt ein Segmenter-Objekt, das Text gemäß Unicode-Regeln aufteilt. Sie geben ein 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-Identifier. Das zweite Argument ist ein Options-Objekt, wobei granularity: "word" dem Segmenter mitteilt, an Wortgrenzen aufzuteilen.
Die segment()-Methode gibt ein iterierbares Objekt zurück, das Segmente enthält. Sie können über Segmente mit for...of 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 Properties:
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 Nicht-Wort-Inhalt ist
Verständnis der isWordLike-Property
Wenn Sie Text nach Wörtern segmentieren, gibt der Segmenter sowohl Wort-Segmente als auch Nicht-Wort-Segmente zurück. Wörter umfassen Buchstaben, Zahlen und ideografische Zeichen. Nicht-Wort-Segmente umfassen Leerzeichen, Interpunktion und andere Trennzeichen.
Die isWordLike-Property gibt an, ob ein Segment ein Wort ist. Diese Property ist true für Segmente, die Wortzeichen enthalten, und false für Segmente, die nur Leerzeichen, Interpunktion oder andere Nicht-Wort-Zeichen 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. So erhalten Sie 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.
Wörter aus Text ohne Leerzeichen extrahieren
Der Segmentierer identifiziert Wortgrenzen in Sprachen ohne Leerzeichen korrekt. Bei chinesischem Text teilt der Segmentierer 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 Segmentierer identifiziert zwei Wörter in diesem Text. Es gibt keine Leerzeichen, aber der Segmentierer versteht chinesische Wortgrenzen und teilt den Text entsprechend.
Bei japanischem Text verarbeitet der Segmentierer die Komplexität gemischter Schriftsysteme 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 Segmentierer teilt diesen Satz in fünf Wortsegmente. Er erkennt, dass Partikeln wie "は" separate Wörter sind und dass zusammengesetzte Wörter wie "天気" einzelne Einheiten bilden.
Bei thailändischem Text identifiziert der Segmentierer 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 Segmentierer identifiziert in dieser Begrüßung korrekt zwei Wörter.
Eine Funktion zur Wortextraktion erstellen
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 für Sprachen mit und 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 unabhängig von der Sprache oder dem Schriftsystem ein Array von Wörtern zurück.
Wörter sprachübergreifend 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 liefert präzise 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 Benutzerwahrnehmung von Wortgrenzen in jeder Sprache.
Ermitteln, welches Wort eine Position enthält
Die Methode containing() findet das Segment, das einen bestimmten Index in der Zeichenkette 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 in Leerzeichen oder Satzzeichen fällt, 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 Satzzeichen und Kontraktionen
Der Segmentierer behandelt Satzzeichen 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 das Gebietsschema die Wortsegmentierung beeinflusst
Das Gebietsschema, das Sie an den Segmentierer übergeben, beeinflusst, wie Wortgrenzen bestimmt werden. Verschiedene Gebietsschemata können unterschiedliche Regeln für denselben Text haben.
Für Sprachen mit klar definierten Wortgrenzregeln stellt das Gebietsschema 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);
// ["你好", "世界"]
Das englische Gebietsschema erkennt chinesische Wortgrenzen nicht und behandelt die gesamte Zeichenkette als ein Wort. Das chinesische Gebietsschema wendet chinesische Wortgrenzregeln an und identifiziert korrekt zwei Wörter.
Verwenden Sie immer das entsprechende Locale für die Sprache des zu segmentierenden Textes.
Wiederverwendbare Segmenter für bessere Performance erstellen
Das Erstellen eines Segmenters ist nicht teuer, aber Sie können Segmenter über mehrere Strings hinweg 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 einmal und verwendet sie für alle Aufrufe von getWords() wieder. Der Segmenter cached Locale-Daten, sodass die Wiederverwendung von Instanzen wiederholte Initialisierungen vermeidet.
Praktisches Beispiel: Einen Worthäufigkeitsanalysator erstellen
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 auf Kleinschreibung und zählt 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 Änderungen.
Browser-Unterstützung prüfen
Die Intl.Segmenter-API erreichte im April 2024 den Baseline-Status. Sie funktioniert in aktuellen Versionen von Chrome, Firefox, Safari und Edge. Ältere Browser unterstützen sie nicht.
Prüfen Sie die Unterstützung, bevor Sie die API verwenden.
if (typeof Intl.Segmenter !== "undefined") {
const segmenter = new Intl.Segmenter("en", { granularity: "word" });
// Use segmenter
} else {
// Fallback for older browsers
}
Für Produktionsanwendungen, die ältere Browser unterstützen sollen, 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: only works for space-separated languages
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.
Häufige Fehler vermeiden
Teilen Sie nicht auf Leerzeichen oder Regex-Muster für mehrsprachigen Text. Diese Ansätze funktionieren nur für eine Teilmenge von Sprachen und versagen bei Chinesisch, Japanisch, Thailändisch und anderen Sprachen ohne Leerzeichen.
Vergessen Sie nicht, nach isWordLike zu filtern, wenn Sie Wörter extrahieren. Ohne diesen Filter erhalten Sie Leerzeichen, Interpunktion und andere Nicht-Wort-Segmente in Ihren Ergebnissen.
Verwenden Sie nicht das falsche Gebietsschema beim Segmentieren von Text. Das Gebietsschema bestimmt, welche Wortgrenzenregeln gelten. Die Verwendung eines englischen Gebietsschemas für chinesischen Text führt zu falschen Ergebnissen.
Gehen Sie nicht davon aus, dass alle Sprachen Wörter auf die gleiche Weise definieren. Wortgrenzen variieren je nach Schriftsystem und linguistischer Konvention. Verwenden Sie gebietsschemabewusste Segmentierung, um diese Unterschiede zu handhaben.
Zählen Sie Wörter nicht mit split(" ").length für internationalen Text. Dies funktioniert nur für durch Leerzeichen getrennte Sprachen und liefert für andere falsche Zählungen.
Wann Wortsegmentierung verwendet werden sollte
Verwenden Sie Wortsegmentierung, wenn Sie Folgendes benötigen:
- Wörter in nutzergenerierten Inhalten über mehrere Sprachen hinweg zählen
- Such- und Hervorhebungsfunktionen implementieren, die mit jedem Schriftsystem funktionieren
- Textanalyse-Tools erstellen, die internationalen Text verarbeiten
- Wortbasierte Navigations- oder Bearbeitungsfunktionen in Texteditoren erstellen
- Schlüsselwörter oder Begriffe aus mehrsprachigen Dokumenten extrahieren
- Wortzahlbegrenzungen in Formularen validieren, die jede Sprache akzeptieren
Verwenden Sie keine Wortsegmentierung, wenn Sie nur Zeichenzählungen benötigen. Verwenden Sie Graphemsegmentierung für Operationen auf Zeichenebene.
Verwenden Sie keine Wortsegmentierung für Satztrennung. Verwenden Sie dafür Granularität auf Satzebene.
Wie Wortsegmentierung in die Internationalisierung passt
Die Intl.Segmenter-API ist Teil der ECMAScript Internationalization API. Andere APIs in dieser Familie behandeln verschiedene Aspekte der Internationalisierung:
Intl.DateTimeFormat: Formatiert Datum und Uhrzeit entsprechend dem GebietsschemaIntl.NumberFormat: Formatiert Zahlen, Währungen und Einheiten entsprechend dem GebietsschemaIntl.Collator: Sortiert und vergleicht Zeichenketten entsprechend dem GebietsschemaIntl.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.