2015-03-16 2 views
1

Я пытаюсь извлечь несколько строк, представляющих некоторые элементы XML из файла. Пользователь предоставляет файл, используя простой <input type="file"> тег, и чем этот файл читается как текст с FileReader и дал в качестве параметра этой функции:javascript indexOf с миллионами совпадений

var relevantDelimiters = [{"begin":"<header>","end":"</header>"} 
,{"begin":" <someElement>","end":"</someElement>"}]; 

function dealWithString(invalidXML) { 
    var validXML = ""; 
    for (var i=0; i<relevantDelimiters.length; i++) { 
    delimiter = relevantDelimiters[i]; 
    while (invalidXML.indexOf(delimiter.begin) != -1) { 
     //while there are relevant elements of this kind left: 
     startPos = invalidXML.indexOf(delimiter.begin); 
     endPos = invalidXML.indexOf(delimiter.end); 
     //append to end result: 
     validXML+=invalidXML.substring(startPos,endPos+delimiter.end.length)+"\n"; 
     //take this item out of the input to process next item 
     invalidXML = invalidXML.replace(invalidXML.substring(startPos,endPos+delimiter.end.length),""); 
    } 
    } 
    //return fixed data 
    return validXML; 
} 

Этот подход, кажется, работает очень хорошо с небольшим количеством совпадений во входном текстовом файле, но при заданном файле 1.5MB скрипт застревает (работает с Google Chrome, что делает его вкладчиком неактивным). Этот файл содержит около миллиона «релевантных элементов», что означает совпадения от relevantDelimiters.

Как это можно оптимизировать?

+3

Вы можете использовать [WebWorker] (https: //developer.mozilla. org/en-US/docs/Web/API/Web_Workers_API/basic_usage), так что пользовательский интерфейс браузера может продолжать работать во время поиска. –

+0

@JuanMendes: Это звучит как * ответ * для меня. –

+0

Довольно тяжелая манипуляция строк здесь, и хотя есть способы улучшить производительность, я задаюсь вопросом, не могли бы вы получить больше ударов за свой доллар, передав его через XSL-шаблон, чтобы удалить нежелательные узлы XML. – wwwmarty

ответ

3

Вместо того, чтобы повторно «брать элемент из ввода», позвонив по нему replace, вы должны использовать второй аргумент для indexOf: fromIndex. Таким образом, он будет искать следующее происхождение после данного индекса, и вы можете прокручивать очень большой вход, не прикасаясь к нему.

function dealWithString(invalidXML) { 
    var validXML = ""; 
    for (var i=0; i<relevantDelimiters.length; i++) { 
    var delimiter = relevantDelimiters[i], 
     pos = 0, 
     startPos; 
    while ((startPos = invalidXML.indexOf(delimiter.begin, pos)) != -1) { 
     //while there are relevant elements of this kind left: 
     var endPos = invalidXML.indexOf(delimiter.end, startPos); 
     // assert(endPos != -1) - otherwise this could go horribly wrong 
     pos = endPos+delimiter.end.length; 
     //append to end result: 
     validXML += invalidXML.slice(startPos, pos) + "\n"; 
    } 
    } 
    return validXML; 
} 
+1

Это, безусловно, улучшит скорость. Может еще понадобиться [идея Хуана] (http://stackoverflow.com/questions/29086481/javascript-indexof-with-millions-of-matches#comment46405953_29086481) * также *, особенно если предупреждение основано на итерациях, а не действует как по крайней мере в некоторых версиях IE, но определенно улучшат скорость. –

+0

Спасибо! Ты жжешь! – user1555863

0

Куда расходуют? Я предполагаю, что вы можете разбить это большое синхронное действие на пару асинхронных надежд. (Каждая пара while-iterations, вы можете сохранить свой индекс и установить тайм-аут до возобновления. Таким образом, вы не блокируете поток пользовательского интерфейса.

+0

Это, вероятно, механизм последней инстанции, но - резко ** замедляет работу, когда браузер занимает 4-10 мс, чтобы перезвонить вам. Веб-работник (где доступно) является гораздо лучшей идеей. –

+1

Веб-работники - это только «лучшая идея», если пользователь не заботится об IE 8 или 9. – brianvaughn

+2

Никто не заботится об IE 8/9 :) – user1555863