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.

3voto

virgo47 Punkte 2173

Vor langer Zeit habe ich dies in Java gemacht und die Lösung eines anderen gefunden, die auf einer einzigen Zeichenkette basiert, die den Teil der Unicode-Tabelle erfasst, der für die Konvertierung wichtig war - der Rest wurde in ? oder ein anderes Ersatzzeichen umgewandelt. Also habe ich versucht, es in JavaScript zu konvertieren. Allerdings bin ich kein JS-Experte :-)

TAB_00C0 = "AAAAAAACEEEEIIII" +
    "DNOOOOO*OUUUUYIs" +
    "aaaaaaaceeeeiiii" +
    "?nooooo/ouuuuy?y" +
    "AaAaAaCcCcCcCcDd" +
    "DdEeEeEeEeEeGgGg" +
    "GgGgHhHhIiIiIiIi" +
    "IiJjJjKkkLlLlLlL" +
    "lLlNnNnNnnNnOoOo" +
    "OoOoRrRrRrSsSsSs" +
    "SsTtTtTtUuUuUuUu" +
    "UuUuWwYyYZzZzZzF";

function stripDiacritics(source) {
    var result = source.split('');
    for (var i = 0; i < result.length; i++) {
        var c = source.charCodeAt(i);
        if (c >= 0x00c0 && c <= 0x017f) {
            result[i] = String.fromCharCode(TAB_00C0.charCodeAt(c - 0x00c0));
        } else if (c > 127) {
            result[i] = '?';
        }
    }
    return result.join('');
}

stripDiacritics("Šupa, o? šžýæøåð")

Damit werden die meisten lateinischen1+2 Unicode-Zeichen umgewandelt. Es ist nicht in der Lage, einzelne Zeichen in mehrere zu übersetzen. Ich weiß nicht, seine Leistung auf JS, in Java ist dies bei weitem die schnellste der gemeinsamen Lösungen (6-50x), gibt es keine Karte, gibt es keine Regex, nichts. Es produziert strenge ASCII-Ausgabe, möglicherweise mit einem Verlust von Informationen, aber die Größe der Ausgabe entspricht die Eingabe.

Ich habe das Snippet mit http://www.webtoolkitonline.com/javascript-tester.html und es produzierte Supa, co? lstczyaoa?? wie erwartet.

1 Stimmen

Das ist ziemlich raffiniert. Danke fürs Teilen!

1 Stimmen

Kürzlich verglich ich dieses mit "Šupa, o? šžýæøåð".normalize ("NFKD").replace (/[\u0300-\u036F]/g, "") und war überrascht, dass normalize + replace (Regex, wohlgemerkt) ist etwa doppelt so schnell! Ich schiebe es darauf, dass diese Dinge Built-Ins sind und massiv optimiert wurden, aber es ist nicht zu leugnen. Die andere Sache ist, dass es nicht genau dasselbe für einige Zeichen tut. Das Ergebnis ist: Supa, co? lstczyæøaHð - also æøð nicht aufgelöst werden, aber andererseits deckt es ð die außerhalb der Reichweite meines Tisches lag. Da ich das weiß, ziehe ich es vor normalize+replace ehrlich gesagt.

2voto

jakov Punkte 601

Wenn Sie eine Sortierung erreichen wollen, bei der "ä" nach "a" kommt und nicht als gleichwertig behandelt wird, können Sie eine Funktion wie meine verwenden.

Sie können das Alphabet jederzeit ändern, um andere oder sogar seltsame Sortierungen zu erhalten. Wenn Sie jedoch möchten, dass einige Buchstaben gleichwertig sind, müssen Sie die Zeichenketten wie folgt manipulieren a = a.replace(/ä/, 'a') oder ähnliches, wie viele bereits oben geantwortet haben. Ich habe die Großbuchstaben eingefügt, falls jemand alle Wörter in Großbuchstaben vor allen Wörtern in Kleinbuchstaben haben möchte (dann muss man die Großbuchstaben weglassen .toLowerCase() ).

function sortbyalphabet(a,b) {
        alphabet = "0123456789AaÀàÁáÂâÃãÄäBbCcÇçDdÈèÉéÊêËëFfGgHhÌìÍíÎîÏïJjKkLlMmNnÑñOoÒòÓóÔôÕõÖöPpQqRrSsTtÙùÚúÛûÜüVvWwXxÝýŸÿZz";
        a = a.toLowerCase();
        b = b.toLowerCase();
        shorterone = (a.length > b.length ? a : b);
        for (i=0; i<shorterone.length; i++){
            diff = alphabet.indexOf(a.charAt(i)) - alphabet.indexOf(b.charAt(i));
            if (diff!=0){
                return diff;
            }
        }
        // sort the shorter first
        return a.length - b.length;
    }
    var n = ["ast", "Äste", "apfel", "äpfel", "à"];
    console.log(n.sort(sortbyalphabet));
    // should return ["apfel", "ast", "à", "äpfel", "äste"]

0 Stimmen

Die Idee ist gut, die Umsetzung kann verbessert werden. 1) Sie haben nicht die var Stichwort. Das bedeutet, dass jede Variable, die Sie deklarieren, global ist. Das ist sicherlich nicht das, was Sie im Sinn hatten (es gibt keinen automatischen Funktionsumfang in JS). Vergessen Sie var macht böse Bugs. 2) Sie sollten einen Abschluss verwenden, anstatt das Alphabet bei jedem Funktionsaufruf neu zu definieren. 3) Sie führen weder eine Typüberprüfung noch strenge Vergleiche durch. -- Ich habe hier eine optimierte Version Ihrer Funktion erstellt: jsperf.com/collation-string-sorting . Sowohl auf Chrome als auch auf IE ist es etwa viermal so schnell wie Ihr Ansatz.

1voto

Kelvin Marques Punkte 31

Ein einfacher und leichter Weg:

function remove-accents(p){
c='áàãâäéèêëíìîïóòõôöúùûüçÁÀÃÂÄÉÈÊËÍÌÎÏÓÒÕÖÔÚÙÛÜÇ';s='aaaaaeeeeiiiiooooouuuucAAAAAEEEEIIIIOOOOOUUUUC';n='';for(i=0;i<p.length;i++){if(c.search(p.substr(i,1))>=0){n+=s.substr(c.search(p.substr(i,1)),1);} else{n+=p.substr(i,1);}} return n;
}

Dann tun Sie dies:

remove-accents("Thís ís ân accêntéd phráse");

Ausgabe:

"This is an accented phrase"

1voto

rmpt Punkte 587

Antwort os Crisalin ist fast perfekt. Ich habe nur die Leistung verbessert, um zu vermeiden, dass bei jedem Durchlauf neue RegExp erstellt werden.

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

Verwendung:

function(str){
    normalizeConversions.forEach(function(normalizeEntry){
        str = str.replace(normalizeEntry.regex, normalizeEntry.clean);
    });
    return str;
};

0 Stimmen

Ich denke, man kann etwas Platz sparen, indem man Regex-Literale verwendet, und Zeichenklassen sind effizienter als Alternativen. Der wirkliche Leistungseinbruch ist jedoch die Ausführung von so vielen Regexen über dieselbe Zeichenfolge. Regex ist langsam. 100 Regexe sind langsam*100. Es ist viel effizienter, eine einzige Regex auszuführen, die auf 100 Zeichen passt, und die Ersetzungen nachzuschlagen, wie es die akzeptierte Antwort tut, als 100 Regexe in einer Schleife auszuführen. Darüber hinaus sind JS-Zeichenfolgen unveränderlich, so dass Sie (Anzahl der Regexe-1) Wegwerf-Zeichenfolgen mit diesem Ansatz zuweisen, die ziemlich verschwenderisch ist, auch.

0 Stimmen

Hier gibt es 2 Dinge: Speicher und Verarbeitungsleistung. In Bezug auf die Speichernutzung haben Sie Recht, dieser Ansatz erfordert mehr Speicher, aber heutzutage haben alle Geräte viel Speicher und es ist nicht so viel Speicher zuzuweisen. Was die Verarbeitungsleistung betrifft, so glaube ich, dass Sie sich irren. Ich vergleiche nicht 100 Zeichen und suche dann nach der Ersetzung. Ich mache genau das Gleiche wie die Crisalin-Antwort, aber anstatt bei jedem Schleifeninkrement eine RegExp zu erstellen, erstelle ich sie einmal und verwende sie bei jedem Aufruf wieder. Benötigt ein wenig mehr Speicher, aber viel schneller.

0 Stimmen

Sie wenden 100 (ok, derzeit 50) Regexe in einer Schleife an und erstellen dabei ständig neue Zeichenfolgen. Das ist ineffizient. Probieren Sie es aus. Versuchen Sie es auch mit langen Zeichenketten.

0voto

Samiul Punkte 150

Ich habe das Problem auf eine andere Weise gelöst, wenn Sie möchten.

Hier habe ich zwei Arrays verwendet, in denen searchChars die ersetzt werden sollen und replaceChars mit den gewünschten Zeichen.

var text = "your input string";
var searchChars = ['Å','Ä','å','Ö','ö']; // add more charecter.
var replaceChars = ['A','A','a','O','o']; // exact same index to searchChars.
var index;
for (var i = 0; i < text.length; i++) {
  if( $.inArray(text[i], searchChars) >-1 ){ // $.inArray() is from jquery.
    index = searchChars.indexOf(text[i]);
    text = text.slice(0, i) + replaceChars[index] + text.slice(i+1,text.length);
  }
}

1 Stimmen

Dies ist äußerst ineffizient. Sie sind gut beraten, wenn Sie eine der anderen Lösungen wählen.

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