Каков оптимальный способ заменить последовательность символов в строке в JavaScript - PullRequest
5 голосов
/ 05 февраля 2009

Я работаю над улучшением производительности функции, которая принимает строку XML и заменяет определенные символы (кодировку) перед возвратом строки. Функция становится заглушенной, поэтому важно запускать ее как можно быстрее. Случай USUAL заключается в том, что ни один из символов не присутствует, поэтому я хотел бы особенно оптимизировать для этого. Как вы увидите в примере кода, заменяемые строки короткие и относительно немного. Исходные строки часто будут короткими (например, 10-20 символов), но могут быть длиннее (например, 200 символов или около того).

До сих пор я гарантировал, что регулярные выражения предварительно скомпилированы, и я исключил вложенные функции, которые замедляли работу (на этом этапе имеют значение частичные миллисекунды.)

var objXMLToString = new XMLToStringClass();
function XMLToStringClass(){
    this.tester= /\\34|\\39|\\62|\\60|\\13\\10|\\09|\\92|&amp/;
    this.replacements=[];
    var self=this;
    function init(){        
        var re = new regexReplacePair(/\\34/g,'"');
        self.replacements.push(re);
        re = new regexReplacePair(/\\39/g,"'");
        self.replacements.push(re);
        re = new regexReplacePair(/\\62/g,">");
        self.replacements.push(re);
        re = new regexReplacePair(/\\60/g,"<");
        self.replacements.push(re);
        re = new regexReplacePair(/\\13\\10/g,"\n");
        self.replacements.push(re);
        re = new regexReplacePair(/\\09/g,"\t");
        self.replacements.push(re);
        re = new regexReplacePair(/\\92/g,"\\");
        self.replacements.push(re);
        re = new regexReplacePair(/\&amp;/g,"&");       
        self.replacements.push(re);     
    }
    init();
}


function regexReplacePair(regex,replacementString){
    this.regex = regex;
    this.replacement = replacementString;
}

String.prototype.XMLToString = function() {
        newString=this;
        if(objXMLToString.tester.test(this)){
            for (var x = 0;x<objXMLToString.replacements.length;x++){
                newString=newString.replace(objXMLToString.replacements[x].regex,objXMLToString.replacements[x].replacement);
            }
            return newString;
        }
        return this;
}
  • Возможно ли, что в этом сценарии String.replace функции будут лучше?
  • В настоящее время я заменяю все символы, если один совпадение символов - возможно ли что тестирование, а затем условно замена будет лучше? Если это так, может ли indexOf быть быстрее, чем регулярное выражение для этого набора данных?

Ответы [ 3 ]

6 голосов
/ 06 февраля 2009

Я проверил вашу оригинальную версию, хэш Ates Gorals, мой улучшенный хеш, версию с использованием переключателя и простое решение. Победитель? Простое решение!

С соответствующими данными (строка из 85 символов)

        original  simple  hash  switch  ag-hash
FF3          194     188   250     240     424
IE7          188     172   641     906    2203
Chrome1      161     156   165     165     225
Opera9       640     625   531     515     734

С несоответствующими данными (строка из 85 символов):

        original  simple  hash  switch  ag-hash
FF3           39       4    34      34      39
IE7          125      15   125     125     156
Chrome1       45       2    54      54      57
Opera9       156      15   156     156     156

(протестировано на моем ноутбуке с Windows XP, 1,7 ГГц, мммм)

Простое решение:

function XMLToString(str) {
    return (str.indexOf("\\")<0 && str.indexOf("&")<0) ? str :
    str
    .replace(/\\34/g,'"')
    .replace(/\\39/g,"'")
    .replace(/\\62/g,">")
    .replace(/\\60/g,"<")
    .replace(/\\13\\10/g,"\n")
    .replace(/\\09/g,"\t")
    .replace(/\\92/g,"\\")
    .replace(/\&amp;/g,"&");               
}

Объяснение:

Сначала выполняется проверка на наличие обратной косой черты или амперсанда (было быстрее использовать indexOf вместо регулярного выражения во всех браузерах). Если нет, строка возвращается без изменений, в противном случае выполняются все замены. В этом случае не имеет большого значения кэшировать регулярные выражения. Я попытался с двумя массивами, один с регулярными выражениями и один с заменами, но это не было большой разницей.

В вашей исходной версии у вас есть тест для всех комбинаций, чтобы определить, должны ли вы делать замены или нет. Это дорого, так как механизм регулярных выражений должен сравнивать каждую позицию с каждой комбинацией. Я упростил это.

Я улучшил Ates Gorals, переместив хеш-объект наружу (чтобы он не создавался и не выбрасывался при каждом вызове внутренней функции), перемещая внутреннюю функцию наружу, чтобы ее можно было использовать повторно вместо выбрасывания.

ОБНОВЛЕНИЕ 1 Исправление: перемещена скобка в тесте амперсанда.

ОБНОВЛЕНИЕ 2

Один из ваших комментариев заставил меня поверить, что вы сами кодируете строку, и если это так, я предлагаю вам переключить кодировку на стандартную, чтобы вы могли использовать встроенные функции.

Вместо "\ dd", где dd - десятичное число, используйте "% xx", где xx - шестнадцатеричное число. Тогда вы можете использовать встроенный decodeURIComponent, который работает намного быстрее и в качестве бонуса может декодировать любые символы, включая юникод.

          matching    non match
FF3          44           3
IE7          93          16
Chrome1     132           1
Opera9      109          16

.

function XMLToString_S1(str) {
    return (str.indexOf("%")<0) ? str : decodeURIComponent(str).replace(/\x0D\x0A/g,"\n")
}

Таким образом, вместо строки типа "\ 09test \ 60 \ 34string \ 34 \ 62 \ 13 \ 10 \" у вас есть строка типа "% 09test% 3c% 22string% 22% 3e% 0d% 0a".

6 голосов
/ 05 февраля 2009

Вы можете использовать поиск по хешу:

str.replace(
    /(\\34|\\39|\\62|\\60|\\13\\10|\\09|\\92|&amp)/g,
    function () {
        return {
            "\\34": "\"",
            "\\39": "'",
            //...
            "&amp": "&"
        }[arguments(1)];
    }
);

Или вы настаиваете на расширении прототипа:

var hash = {
    "\\34": "\"",
    "\\39": "'",
    //...
    "&amp": "&"
};

String.prototype.XMLToString = function () {
    return this.replace(
        /(\\34|\\39|\\62|\\60|\\13\\10|\\09|\\92|&amp)/g,
        function () { return hash[arguments(1)]; }
    }
);

Это может быть быстрее (нужно тестировать):

String.prototype.XMLToString = function () {
    var s = this;

    for (var r in hash) {
        s = s.split(r).join(hash[r]);
    }

    return s;
);

Обновление

Вы можете создать свое регулярное выражение из хеша:

var arr = [];

for (var r in hash) {
    arr.push(r);
}

var re = new RegExp("(" + arr.join("|") + ")", "g");

, а затем использовать его как:

s = s.replace(re, function () { ... });
2 голосов
/ 05 февраля 2009

Вот мой удар по рефакторингу вашего кода

  • Сделал objXMLToString статическим объектом - его не нужно создавать
  • Избавился от внутренней функции init () - вместо нее использовался литерал массива
  • Преобразованный объект regexReplacePair с литералами массива
  • Преобразован для цикла в цикл while (обычно быстрее)
  • Одиночная точка возврата
  • Scoped newString переменная (теперь она больше не глобальная)

Вот код

var objXMLToString = {
     tester: /\\34|\\39|\\62|\\60|\\13\\10|\\09|\\92|&amp/
    ,replacements: [
         [/\\34/g,'"']
        ,[/\\39/g,"'"]
        ,[/\\62/g,">"]
        ,[/\\60/g,"<"]
        ,[/\\13\\10/g,"\n"]
        ,[/\\09/g,"\t"]
        ,[/\\92/g,"\\"]
        ,[/\&amp;/g,"&"]
    ]
}

String.prototype.XMLToString = function()
{
        var newString = this;
        if ( objXMLToString.tester.test( this ) )
        {
                var x = 0, replacer;
                while ( replacer = objXMLToString.replacements[x++] )
                {
                        newString = newString.replace( replacer[0], replacer[1] );
                }
        }
        return newString;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...