Zeichenketten vergleichen unter Ignorierung von Akzentzeichen

Erfahren Sie, wie Sie Zeichenketten vergleichen können, während Sie diakritische Zeichen mit JavaScript-Normalisierung und Intl.Collator ignorieren

Einführung

Beim Entwickeln von Anwendungen, die mit mehreren Sprachen arbeiten, müssen Sie oft Zeichenketten vergleichen, die Akzentzeichen enthalten. Ein Benutzer, der nach "cafe" sucht, sollte Ergebnisse für "café" finden. Eine Benutzernamenprüfung für "Jose" sollte mit "José" übereinstimmen. Der standardmäßige Zeichenkettenvergleich behandelt diese als unterschiedliche Zeichenketten, aber Ihre Anwendungslogik muss sie als gleich behandeln.

JavaScript bietet zwei Ansätze zur Lösung dieses Problems. Sie können Zeichenketten normalisieren und Akzentzeichen entfernen oder die integrierte Collation-API verwenden, um Zeichenketten mit spezifischen Sensitivitätsregeln zu vergleichen.

Was sind Akzentzeichen

Akzentzeichen sind Symbole, die über, unter oder durch Buchstaben platziert werden, um deren Aussprache oder Bedeutung zu modifizieren. Diese Zeichen werden Diakritika genannt. Häufige Beispiele sind der Akut in "é", die Tilde in "ñ" und das Umlautzeichen in "ü".

In Unicode können diese Zeichen auf zwei Arten dargestellt werden. Ein einzelner Codepunkt kann das vollständige Zeichen darstellen, oder mehrere Codepunkte können einen Grundbuchstaben mit einem separaten Akzentzeichen kombinieren. Der Buchstabe "é" kann als U+00E9 oder als "e" (U+0065) plus einem kombinierenden Akut (U+0301) gespeichert werden.

Wann Akzentzeichen bei Vergleichen ignoriert werden sollten

Die Suchfunktionalität ist der häufigste Anwendungsfall für akzentunempfindliche Vergleiche. Benutzer, die Abfragen ohne Akzentzeichen eingeben, erwarten, Inhalte zu finden, die akzentuierte Zeichen enthalten. Eine Suche nach "Muller" sollte "Müller" finden.

Die Validierung von Benutzereingaben erfordert diese Fähigkeit, wenn geprüft wird, ob Benutzernamen, E-Mail-Adressen oder andere Kennungen bereits existieren. Sie möchten doppelte Konten für "maria" und "maría" verhindern.

Groß-/Kleinschreibungsunempfindliche Vergleiche müssen oft gleichzeitig Akzente ignorieren. Wenn Sie prüfen, ob zwei Zeichenketten unabhängig von der Groß-/Kleinschreibung übereinstimmen, möchten Sie in der Regel auch Akzentunterschiede ignorieren.

Akzentzeichen mittels Normalisierung entfernen

Der erste Ansatz konvertiert Zeichenketten in eine normalisierte Form, bei der Grundbuchstaben und Akzentzeichen getrennt werden, und entfernt dann die Akzentzeichen.

Unicode-Normalisierung konvertiert Zeichenketten in eine Standardform. Die NFD-Form (Kanonische Dekomposition) trennt kombinierte Zeichen in ihre Grundbuchstaben und kombinierenden Zeichen. Die Zeichenkette "café" wird zu "cafe" gefolgt von einem kombinierenden Akut-Akzentzeichen.

Nach der Normalisierung können Sie die kombinierenden Zeichen mit einem regulären Ausdruck entfernen. Der Unicode-Bereich U+0300 bis U+036F enthält kombinierende diakritische Zeichen.

function removeAccents(str) {
  return str.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
}

const text1 = 'café';
const text2 = 'cafe';

const normalized1 = removeAccents(text1);
const normalized2 = removeAccents(text2);

console.log(normalized1 === normalized2); // true
console.log(normalized1); // "cafe"

Diese Methode liefert Zeichenketten ohne Akzentzeichen, die Sie mit Standard-Gleichheitsoperatoren vergleichen können.

Sie können dies mit der Kleinbuchstabenkonvertierung für Groß-/Kleinschreibung-unabhängige und Akzent-unabhängige Vergleiche kombinieren.

function normalizeForComparison(str) {
  return str.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
}

const search = 'muller';
const name = 'Müller';

console.log(normalizeForComparison(search) === normalizeForComparison(name)); // true

Dieser Ansatz funktioniert gut, wenn Sie die normalisierte Version von Zeichenketten für effizientes Suchen speichern oder indizieren müssen.

Zeichenketten mit Intl.Collator vergleichen

Der zweite Ansatz verwendet die Intl.Collator-API, die einen sprachspezifischen Zeichenkettenvergleich mit konfigurierbaren Sensitivitätsstufen bietet.

Das Intl.Collator-Objekt vergleicht Zeichenketten nach sprachspezifischen Regeln. Die Sensitivitätsoption steuert, welche Unterschiede beim Vergleich von Zeichenketten relevant sind.

Die Sensitivitätsstufe "base" ignoriert sowohl Akzentzeichen als auch Unterschiede in der Groß-/Kleinschreibung. Zeichenketten, die sich nur in Akzenten oder Großschreibung unterscheiden, werden als gleich betrachtet.

const collator = new Intl.Collator('en', { sensitivity: 'base' });

console.log(collator.compare('café', 'cafe')); // 0 (gleich)
console.log(collator.compare('Café', 'cafe')); // 0 (gleich)
console.log(collator.compare('café', 'caff')); // -1 (erste kommt vor zweiter)

Die compare-Methode gibt 0 zurück, wenn Zeichenketten gleich sind, eine negative Zahl, wenn die erste Zeichenkette vor der zweiten kommt, und eine positive Zahl, wenn die erste Zeichenkette nach der zweiten kommt.

Sie können dies für Gleichheitsprüfungen oder zum Sortieren von Arrays verwenden.

const collator = new Intl.Collator('en', { sensitivity: 'base' });

function areEqualIgnoringAccents(str1, str2) {
  return collator.compare(str1, str2) === 0;
}

console.log(areEqualIgnoringAccents('José', 'Jose')); // true
console.log(areEqualIgnoringAccents('naïve', 'naive')); // true

Zum Sortieren können Sie die compare-Methode direkt an Array.sort übergeben.

const names = ['Müller', 'Martinez', 'Muller', 'Márquez'];
const collator = new Intl.Collator('en', { sensitivity: 'base' });

names.sort(collator.compare);
console.log(names); // Gruppiert Varianten zusammen

Die Intl.Collator-API bietet weitere Sensitivitätsstufen für verschiedene Anwendungsfälle.

Die Stufe "accent" ignoriert die Groß-/Kleinschreibung, berücksichtigt aber Akzentunterschiede. "Café" ist gleich "café", aber nicht "cafe".

const accentCollator = new Intl.Collator('en', { sensitivity: 'accent' });
console.log(accentCollator.compare('Café', 'café')); // 0 (gleich)
console.log(accentCollator.compare('café', 'cafe')); // 1 (nicht gleich)

Die Stufe "case" ignoriert Akzente, berücksichtigt aber Unterschiede in der Groß-/Kleinschreibung. "café" ist gleich "cafe", aber nicht "Café".

const caseCollator = new Intl.Collator('en', { sensitivity: 'case' });
console.log(caseCollator.compare('café', 'cafe')); // 0 (gleich)
console.log(caseCollator.compare('café', 'Café')); // -1 (nicht gleich)

Die Stufe "variant" berücksichtigt alle Unterschiede. Dies ist das Standardverhalten.

const variantCollator = new Intl.Collator('en', { sensitivity: 'variant' });
console.log(variantCollator.compare('café', 'cafe')); // 1 (nicht gleich)

Wählen Sie zwischen Normalisierung und Kollation

Beide Methoden liefern korrekte Ergebnisse für akzentunempfindliche Vergleiche, haben jedoch unterschiedliche Eigenschaften.

Die Normalisierungsmethode erstellt neue Zeichenketten ohne Akzentzeichen. Verwenden Sie diesen Ansatz, wenn Sie die normalisierten Versionen speichern oder indexieren müssen. Suchmaschinen und Datenbanken speichern oft normalisierten Text für effiziente Suchen.

Die Intl.Collator-Methode vergleicht Zeichenketten, ohne sie zu modifizieren. Verwenden Sie diesen Ansatz, wenn Sie Zeichenketten direkt vergleichen müssen, wie beim Prüfen auf Duplikate oder beim Sortieren von Listen. Der Collator berücksichtigt sprachspezifische Sortierregeln, die einfache Zeichenkettenvergleiche nicht handhaben können.

Leistungsüberlegungen variieren je nach Anwendungsfall. Das einmalige Erstellen eines Collator-Objekts und dessen Wiederverwendung ist effizient für mehrere Vergleiche. Das Normalisieren von Zeichenketten ist effizient, wenn Sie einmal normalisieren und viele Male vergleichen.

Die Normalisierungsmethode entfernt Akzentinformationen dauerhaft. Die Kollationsmethode bewahrt die ursprünglichen Zeichenketten, während sie sie nach den von Ihnen angegebenen Regeln vergleicht.

Arrays mit akzentunempfindlicher Suche filtern

Ein häufiger Anwendungsfall ist das Filtern eines Arrays von Elementen basierend auf Benutzereingaben, wobei Akzentunterschiede ignoriert werden.

const products = [
  { name: 'Café Latte', price: 4.50 },
  { name: 'Crème Brûlée', price: 6.00 },
  { name: 'Croissant', price: 3.00 },
  { name: 'Café Mocha', price: 5.00 }
];

function searchProducts(query) {
  const collator = new Intl.Collator('en', { sensitivity: 'base' });

  return products.filter(product => {
    return collator.compare(product.name.slice(0, query.length), query) === 0;
  });
}

console.log(searchProducts('cafe'));
// Gibt sowohl Café Latte als auch Café Mocha zurück

Für Teilstring-Abgleiche funktioniert der Normalisierungsansatz besser.

function removeAccents(str) {
  return str.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
}

function searchProducts(query) {
  const normalizedQuery = removeAccents(query.toLowerCase());

  return products.filter(product => {
    const normalizedName = removeAccents(product.name.toLowerCase());
    return normalizedName.includes(normalizedQuery);
  });
}

console.log(searchProducts('creme'));
// Gibt Crème Brûlée zurück

Dieser Ansatz prüft, ob der normalisierte Produktname den normalisierten Suchbegriff als Teilstring enthält.

Texteingabe-Abgleich verarbeiten

Bei der Validierung von Benutzereingaben gegen vorhandene Daten benötigen Sie einen akzentunempfindlichen Vergleich, um Verwirrung und Duplikate zu vermeiden.

const existingUsernames = ['José', 'María', 'François'];

function isUsernameTaken(username) {
  const collator = new Intl.Collator('en', { sensitivity: 'base' });

  return existingUsernames.some(existing =>
    collator.compare(existing, username) === 0
  );
}

console.log(isUsernameTaken('jose')); // true
console.log(isUsernameTaken('Maria')); // true
console.log(isUsernameTaken('francois')); // true
console.log(isUsernameTaken('pierre')); // false

Dies verhindert, dass Benutzer Konten mit Namen erstellen, die sich nur in Akzenten oder Großschreibung von bestehenden Konten unterscheiden.

Browser- und Umgebungsunterstützung

Die Methode String.prototype.normalize wird in allen modernen Browsern und Node.js-Umgebungen unterstützt. Internet Explorer unterstützt diese Methode nicht.

Die Intl.Collator-API wird in allen modernen Browsern und Node.js-Versionen unterstützt. Internet Explorer 11 bietet teilweise Unterstützung.

Beide Ansätze funktionieren zuverlässig in aktuellen JavaScript-Umgebungen. Wenn Sie ältere Browser unterstützen müssen, benötigen Sie Polyfills oder alternative Implementierungen.

Einschränkungen bei der Akzententfernung

Einige Sprachen verwenden diakritische Zeichen, um eigenständige Buchstaben zu erzeugen, nicht nur Akzentvariationen. Im Türkischen sind "i" und "ı" verschiedene Buchstaben. Im Deutschen ist "ö" ein eigenständiger Vokal, kein akzentuiertes "o".

Das Entfernen von Akzenten ändert in diesen Fällen die Bedeutung. Überlegen Sie, ob ein akzentunempfindlicher Vergleich für Ihren Anwendungsfall und Ihre Zielsprachen angemessen ist.

Der Kollationsansatz behandelt diese Fälle besser, da er sprachspezifischen Regeln folgt. Die Angabe der korrekten Locale im Konstruktor von Intl.Collator gewährleistet kulturell angemessene Vergleiche.

const turkishCollator = new Intl.Collator('tr', { sensitivity: 'base' });
const germanCollator = new Intl.Collator('de', { sensitivity: 'base' });

Berücksichtigen Sie immer die Sprachen, die Ihre Anwendung unterstützt, wenn Sie eine Vergleichsstrategie wählen.