135 Stimmen

Alle akzentuierten Zeichen in einer Zeichenkette effizient ersetzen?

Für die Umsetzung eines armen Mannes von in der Nähe von -korrekte Sortierung auf der Client-Seite Ich brauche eine JavaScript-Funktion, die effizient Ersetzung einzelner Zeichen in einer Zeichenkette.

Hier ist, was ich meine (beachten Sie, dass dies für den deutschen Text gilt, andere Sprachen sortieren anders):

native sorting gets it wrong: a b c o u z ä ö ü
collation-correct would be:   a ä b c o ö u ü z

Im Grunde muss ich alle Vorkommen von "ä" in einer gegebenen Zeichenkette durch "a" (und so weiter) ersetzen. Auf diese Weise würde das Ergebnis der nativen Sortierung sehr nahe an dem liegen, was ein Benutzer erwarten würde (oder was eine Datenbank zurückgeben würde).

In anderen Sprachen ist dies möglich: Python-Lieferungen str.translate() , in Perl gibt es tr/…/…/ , XPath hat eine Funktion translate() , ColdFusion hat ReplaceList() . Aber was ist mit JavaScript?

Hier ist, was ich im Moment habe.

// s would be a rather short string (something like 
// 200 characters at max, most of the time much less)
function makeSortString(s) {
  var translate = {
    "ä": "a", "ö": "o", "ü": "u",
    "Ä": "A", "Ö": "O", "Ü": "U"   // probably more to come
  };
  var translate_re = /[öäüÖÄÜ]/g;
  return ( s.replace(translate_re, function(match) { 
    return translate[match]; 
  }) );
}

Zunächst einmal gefällt mir nicht, dass die Regex bei jedem Funktionsaufruf neu aufgebaut wird. Ich schätze, dass eine Schließung in dieser Hinsicht helfen kann, aber ich scheine aus irgendeinem Grund nicht den Dreh raus zu haben.

Fällt jemandem etwas Effizienteres ein?


Die folgenden Antworten fallen in zwei Kategorien:

  1. Funktionen zur Ersetzung von Zeichenketten mit unterschiedlichem Grad an Vollständigkeit und Effizienz (worum ich ursprünglich gebeten hatte)
  2. A späte Erwähnung von String#localeCompare die jetzt weitgehend unterstützt unter den JS-Engines (nicht so sehr zum Zeitpunkt der Frage) und könnte diese Art von Problem viel eleganter lösen.

12 Stimmen

Sie liegen falsch mit Ihrer Annahme, dass ein Benutzer erwartet, dass "ä" mit "a" sortiert wird. Das schwedische Alphabet hat 29 Buchstaben: abcdefghijklmnopqrstuvwxyzåäö und das dänische/norwegische auch: abcdefghijklmnopqrstuvwxyzæøå. Die erwartete Reihenfolge ist: "Apelsin", "Banan", "Äpple".

1 Stimmen

Ich weiß. Die Lösung war für die Sortierung deutscher Texte gedacht. Auch dort ist sie nicht richtig aber gut genug für den Anwendungsfall. Diese Frage war nie als Suche nach dem "löst alle Probleme"-Algorithmus gedacht.

1 Stimmen

Ich habe die Frage ein wenig umformuliert, um das von Anfang an klar zu machen.

21voto

Takit Isy Punkte 8886

Ich kann mir keine einfachere Methode zur effizienten Entfernung von tous diakritischen Zeichen aus einer Zeichenkette als mit dieser erstaunliche Lösung .

Sehen Sie es in Aktion:

var string = "öäüÖÄÜ";

var string_norm = string.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
console.log(string_norm);

4 Stimmen

Dies wurde bereits in einer anderen Antwort in diesem Thread behandelt. stackoverflow.com/a/23767389/18771

1 Stimmen

@Tomalak Stimmt, ich habe es nicht bemerkt. Wie auch immer, ich lösche meine Antwort nicht, weil ich denke, dass es besser ist, "NFD" statt "NFKD" zu verwenden. Außerdem habe ich ein Snippet :p

1 Stimmen

Eine nette und einfache Lösung. Danke @TakitIsy

18voto

Martin_Lakes Punkte 308

Ich denke, dass dies ein wenig sauberer/besser funktionieren könnte (obwohl ich die Leistung nicht getestet habe):

String.prototype.stripAccents = function() {
    var translate_re = /[àáâãäçèéêëìíîïñòóôõöùúûüýÿÀÁÂÃÄÇÈÉÊËÌÍÎÏÑÒÓÔÕÖÙÚÛÜÝ]/g;
    var translate = 'aaaaaceeeeiiiinooooouuuuyyAAAAACEEEEIIIINOOOOOUUUUY';
    return (this.replace(translate_re, function(match){
        return translate.substr(translate_re.source.indexOf(match)-1, 1); })
    );
};

Wenn Ihnen die Leistung immer noch zu wichtig ist, können Sie auch das Beste aus beiden Welten wählen:

String.prototype.stripAccents = function() {
    var in_chrs =  'àáâãäçèéêëìíîïñòóôõöùúûüýÿÀÁÂÃÄÇÈÉÊËÌÍÎÏÑÒÓÔÕÖÙÚÛÜÝ',
        out_chrs = 'aaaaaceeeeiiiinooooouuuuyyAAAAACEEEEIIIINOOOOOUUUUY', 
        transl = {};
    eval('var chars_rgx = /['+in_chrs+']/g');
    for(var i = 0; i < in_chrs.length; i++){ transl[in_chrs.charAt(i)] = out_chrs.charAt(i); }
    return this.replace(chars_rgx, function(match){
        return transl[match]; });
};

EDIT (by @Tomalak)

Ich weiß die Idee zu schätzen. Allerdings gibt es mehrere Dinge falsch mit der Umsetzung, wie in dem Kommentar unten skizziert.

Ich würde es folgendermaßen umsetzen.

var stripAccents = (function () {
  var in_chrs   = 'àáâãäçèéêëìíîïñòóôõöùúûüýÿÀÁÂÃÄÇÈÉÊËÌÍÎÏÑÒÓÔÕÖÙÚÛÜÝ',
      out_chrs  = 'aaaaaceeeeiiiinooooouuuuyyAAAAACEEEEIIIINOOOOOUUUUY', 
      chars_rgx = new RegExp('[' + in_chrs + ']', 'g'),
      transl    = {}, i,
      lookup    = function (m) { return transl[m] || m; };

  for (i=0; i<in_chrs.length; i++) {
    transl[ in_chrs[i] ] = out_chrs[i];
  }

  return function (s) { return s.replace(chars_rgx, lookup); }
})();

0 Stimmen

Warum glauben Sie, dass dies besser funktioniert? Ich nehme an, die Objektsuche ist viel schneller als String.indexOf() .

0 Stimmen

Tomalak, fügte ich einen anderen Weg, es zu tun, die das Beste aus beiden Welten (Lesbarkeit und Leistung) sammelt, ich könnte schließlich nehmen es einen Schritt weiter und Cache das char_rgx Objekt, aber ich glaube nicht, dass es viel Sinn macht, es sei denn, wenn mit Echtzeit-Präzision arbeiten...

3 Stimmen

Es tut mir leid, aber an diesem Code sind mehrere Dinge falsch. Erstens, die unangemessene Verwendung von eval() . Es gibt new RegExp() dafür. Zweitens wird der String-Prototyp geändert. Das Ändern von eingebauten Datentypen ist weithin verpönt. Drittens führt die Funktion bei jedem Aufruf eine for-each-character-Schleife aus. Genau das wollte ich von vornherein vermeiden. Das bedeutet, dass die Lesbarkeit auf Kosten der Leistung verbessert wird, was ich für einen schlechten Kompromiss halte. Ich schätze die Idee, aber die Ausführung ist suboptimal :)

16voto

Tomalak Punkte 320467

Auf der Grundlage der Lösung von Jason Bunting verwende ich jetzt Folgendes.

Die ganze Sache ist für die jQuery tablesorter plug-in : Für die (nahezu korrekte) Sortierung von nicht-englischen Tabellen mit dem Tablesorter-Plugin ist es notwendig, eine eigene textExtraction Funktion .

Diese hier:

  • übersetzt die gebräuchlichsten akzentuierten Buchstaben in unakzentuierte (die Liste der unterstützten Buchstaben ist leicht erweiterbar)
  • ändert Daten im deutschen Format ( 'dd.mm.yyyy' ) in ein anerkanntes Format ( 'yyyy-mm-dd' )

Achten Sie darauf, die JavaScript-Datei in UTF-8-Kodierung zu speichern, sonst funktioniert sie nicht.

// file encoding must be UTF-8!
function getTextExtractor()
{
  return (function() {
    var patternLetters = /[öäüÖÄÜáàâéèêúùûóòôÁÀÂÉÈÊÚÙÛÓÒÔß]/g;
    var patternDateDmy = /^(?:\D+)?(\d{1,2})\.(\d{1,2})\.(\d{2,4})$/;
    var lookupLetters = {
      "ä": "a", "ö": "o", "ü": "u",
      "Ä": "A", "Ö": "O", "Ü": "U",
      "á": "a", "à": "a", "â": "a",
      "é": "e", "è": "e", "ê": "e",
      "ú": "u", "ù": "u", "û": "u",
      "ó": "o", "ò": "o", "ô": "o",
      "Á": "A", "À": "A", "Â": "A",
      "É": "E", "È": "E", "Ê": "E",
      "Ú": "U", "Ù": "U", "Û": "U",
      "Ó": "O", "Ò": "O", "Ô": "O",
      "ß": "s"
    };
    var letterTranslator = function(match) { 
      return lookupLetters[match] || match;
    }

    return function(node) {
      var text = $.trim($(node).text());
      var date = text.match(patternDateDmy);
      if (date)
        return [date[3], date[2], date[1]].join("-");
      else
        return text.replace(patternLetters, letterTranslator);
    }
  })();
}

Sie können ihn wie folgt verwenden:

$("table.sortable").tablesorter({ 
  textExtraction: getTextExtractor()
});

0 Stimmen

Ich weiß nicht, ob jemand meinen Kommentar sehen wird, aber ich brauche die gleiche Funktion für einige akzentuierte Buchstaben in Portugiesisch und ich schaffe es nicht, sie zum Laufen zu bringen. Sollen die betreffenden Buchstaben in meiner php-Datei durch den 'html-Code' aufgerufen werden: Í oder durch direkte Eingabe des 'Í'-Buchstabens? Ich habe beides ausprobiert, nichts funktioniert. Und ja, ich habe die js-Funktion geändert, um meine Bedürfnisse mit den Í- und í-Buchstaben zu erfüllen, und meine js ist utf-8 kodiert.

1 Stimmen

@kevin: Natürlich bemerkt jemand den Kommentar ;-) Das Zeichen in deinem HTML (das von der PHP-Datei erzeugt wird, nehme ich an) kann sein &Iacute; oder die eigentliche Í . Es macht keinen Unterschied, solange die Kodierungseinstellungen korrekt sind (tatsächliche PHP-Dateikodierung, vom PHP-Server wahrgenommene Dateikodierung, HTTP Content-Type-Header, HTML-Meta-Tags). Die Verwendung der HTML-Entität dürfte am sichersten sein. Wenn die .js-Datei UTF-8-kodiert ist, muss sie als solche ausgegeben werden ( text/javascript; Charset=UTF-8 ), dann sollte alles in Ordnung sein.

0 Stimmen

Danke, dass du es bemerkt hast ;-), ich habe es überprüft und auf verschiedene Arten versucht, was du gesagt hast, es geht einfach nicht. Könnte das daran liegen, dass andere js-Dateien in der gleichen php-Seite aufgerufen werden? Wenn du einen Blick darauf werfen willst, es ist hier: schulz-al.tempsite.ws/br/?page_id=51 . Vielen Dank für die Hilfe, ich weiß das zu schätzen.

16voto

Die vollständige Lösung für Ihr Anliegen lautet:

function convert_accented_characters(str){
    var conversions = new Object();
    conversions['ae'] = 'ä|æ|';
    conversions['oe'] = 'ö|œ';
    conversions['ue'] = 'ü';
    conversions['Ae'] = 'Ä';
    conversions['Ue'] = 'Ü';
    conversions['Oe'] = 'Ö';
    conversions['A'] = 'À|Á|Â|Ã|Ä|Å|||||';
    conversions['a'] = 'à|á|â|ã|å||||||ª';
    conversions['C'] = 'Ç||||';
    conversions['c'] = 'ç||||';
    conversions['D'] = 'Ð||';
    conversions['d'] = 'ð||';
    conversions['E'] = 'È|É|Ê|Ë|||||';
    conversions['e'] = 'è|é|ê|ë|||||';
    conversions['G'] = '|||';
    conversions['g'] = '|||';
    conversions['H'] = '|';
    conversions['h'] = '|';
    conversions['I'] = 'Ì|Í|Î|Ï||||||';
    conversions['i'] = 'ì|í|î|ï||||||';
    conversions['J'] = '';
    conversions['j'] = '';
    conversions['K'] = '';
    conversions['k'] = '';
    conversions['L'] = '||||';
    conversions['l'] = '||||';
    conversions['N'] = 'Ñ|||';
    conversions['n'] = 'ñ||||';
    conversions['O'] = 'Ò|Ó|Ô|Õ||||||Ø|';
    conversions['o'] = 'ò|ó|ô|õ||||||ø||º';
    conversions['R'] = '||';
    conversions['r'] = '||';
    conversions['S'] = '|||Š';
    conversions['s'] = '|||š|';
    conversions['T'] = '||';
    conversions['t'] = '||';
    conversions['U'] = 'Ù|Ú|Û||||||||||||';
    conversions['u'] = 'ù|ú|û||||||||||||';
    conversions['Y'] = 'Ý|Ÿ|';
    conversions['y'] = 'ý|ÿ|';
    conversions['W'] = '';
    conversions['w'] = '';
    conversions['Z'] = '||Ž';
    conversions['z'] = '||ž';
    conversions['AE'] = 'Æ|';
    conversions['ss'] = 'ß';
    conversions['IJ'] = '';
    conversions['ij'] = '';
    conversions['OE'] = 'Œ';
    conversions['f'] = 'ƒ';
    for(var i in conversions){
        var re = new RegExp(conversions[i],"g");
        str = str.replace(re,i);
    }
    return str;
}

12voto

undefined Punkte 5820

Wenn Sie speziell nach einer Möglichkeit suchen, akzentuierte Zeichen in nicht akzentuierte Zeichen umzuwandeln, und nicht nach einer Möglichkeit, akzentuierte Zeichen zu sortieren, kann die Funktion String.localeCompare mit ein wenig Trickserei so manipuliert werden, dass sie die grundlegenden lateinischen Zeichen findet, die den erweiterten Zeichen entsprechen. Zum Beispiel könnten Sie einen menschenfreundlichen URL-Slug aus einem Seitentitel erzeugen wollen. In diesem Fall können Sie etwa so vorgehen:

var baseChars = [];
for (var i = 97; i < 97 + 26; i++) {
  baseChars.push(String.fromCharCode(i));
}

//if needed, handle fancy compound characters
baseChars = baseChars.concat('ss,aa,ae,ao,au,av,ay,dz,hv,lj,nj,oi,ou,oo,tz,vy'.split(','));

function isUpperCase(c) { return c !== c.toLocaleLowerCase() }

function toBaseChar(c, opts) {
  opts = opts || {};
  //if (!('nonAlphaChar' in opts)) opts.nonAlphaChar = '';
  //if (!('noMatchChar' in opts)) opts.noMatchChar = '';
  if (!('locale' in opts)) opts.locale = 'en';

  var cOpts = {sensitivity: 'base'};

  //exit early for any non-alphabetical character
  if (c.localeCompare('9', opts.locale, cOpts) <= 0) return opts.nonAlphaChar === undefined ? c : opts.nonAlphaChar;

  for (var i = 0; i < baseChars.length; i++) {
    var baseChar = baseChars[i];

    var comp = c.localeCompare(baseChar, opts.locale, cOpts);
    if (comp == 0) return (isUpperCase(c)) ? baseChar.toUpperCase() : baseChar;
  }

  return opts.noMatchChar === undefined ? c : opts.noMatchChar;
}

function latinify(str, opts) {
  return str.replace(/[^\w\s\d]/g, function(c) {
    return toBaseChar(c, opts);
  })
}

// Example:
console.log(latinify('eština Tshesenstsestotse Tshivena Emigliàn–Rumagnòl Slovenšina Português Ting Vit Straße'))

// "Cestina Tsehesenestsestotse Tshivenda Emiglian–Rumagnol Slovenscina Portugues Tieng Viet Strasse"

Dies sollte recht gut funktionieren, aber wenn eine weitere Optimierung erforderlich wäre, könnte eine binäre Suche mit localeCompare als Komparator, um das Basiszeichen zu finden. Beachten Sie, dass die Groß- und Kleinschreibung beibehalten wird, und Optionen erlauben entweder das Beibehalten, Ersetzen oder Entfernen von Zeichen, die nicht alphabetisch sind oder keine passenden lateinischen Zeichen haben, durch die sie ersetzt werden können. Diese Implementierung ist schneller und flexibler und sollte mit neuen Zeichen funktionieren, sobald sie hinzugefügt werden. Der Nachteil ist, dass zusammengesetzte Zeichen wie '' speziell behandelt werden müssen, wenn sie unterstützt werden sollen.

2 Stimmen

Das ist sehr schön. Eine Schande, dass späte Antworten auf alte Themen so wenig Beachtung finden.

2 Stimmen

Das ist mit Sicherheit die beste Antwort. Sollte mehr Stimmen bekommen (ich habe meine!)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X