У меня есть этот очень огромный XML-файл размера 2.8 ГБ. Это - дамп статей польской Википедии. Размер этого файла очень проблематичен для меня. Задача состоит в том, чтобы искать этот файл некоторый большой объем данных. Все, что я имею, является заголовками статей. Я думал, что мог отсортировать это заголовки и использовать один линейный цикл через файл. Идея не так плоха, но статьи не отсортированы в алфавитном порядке. Они отсортированы по идентификатору, который я не знаю априорно.
Так, мое долгое размышление должно было сделать индекс того файла. Сохранить в другом файле (или база данных) строки в следующем формате: title;id;index
(возможно, без идентификатора). Я мой другой вопрос я обратился за помощью с этим. Гипотеза была то, что, если у меня был индекс необходимого тега, я мог бы использовать просто простой Seek
метод для перемещения курсора в файле, не читая все содержание, и т.д. Для меньших файлов я думаю, что это могло хорошо работать. Но на моем компьютере (ноутбук, C2D proc, Win7, VS2008) я получаю ошибку, что приложение не отвечает.
В моей программе я читаю каждую строку из файла и проверяю, содержит ли это тег, в котором я нуждаюсь. Я также считаю все байты, я считал и сохраняю строки в упомянутом выше формате. Таким образом, в то время как индексация программы зависается. Но до того времени индексный файл результата составляет 36.2 МБ, и последний индекс похож 2,872,765,202 (B), в то время как целый XML-файл является 3,085,439,630 B долго.
Моя треть думала, должен был разделить файл на мелкие кусочки. Чтобы быть точными в 26 частей (существует 26 букв на латинском языке), каждый содержащий только записи, запускающиеся для той же буквы, например, в a.xml все записи, которые заголовки запускают в букве "A". Заключительные файлы были бы похожи на десятки МБ, макс. приблизительно 200 МБ, я думаю. Но существует та же проблема с чтением целого файла.
Для чтения файла, я использовал, вероятно, самый быстрый путь: использование StreamReader
. Я считал где-нибудь это StreamReader
и XmlReader
класс от System.Xml
самые быстрые методы. StreamReader
еще быстрее это XmlReader
. Очевидно, что я не могу загрузить весь этот файл в память. Я установил только 3 ГБ RAM, и Win7 берет как 800MB-1GB, когда полностью загружено.
Таким образом, я обращаюсь за помощью. Что должно сделать лучшее. Дело в том, что поиск этот XML-файл должен быть быстрым. Должен быстрее затем загружать единственные страницы Wikipedia в формате HTML. Я даже не уверен, возможно ли это.
Возможно, загрузите все необходимое содержание в базу данных? Возможно, это было бы быстрее? Но тем не менее я должен буду считать целый файл как наименьшее однажды.
Я не уверен, существуют ли некоторые пределы приблизительно 1 длина вопроса, но я помещу здесь также образец своего исходного кода индексации.
while (reading)
{
if (!reader.EndOfStream)
{
line = reader.ReadLine();
fileIndex += enc.GetByteCount(line) + 2; //+2 - to cover characters \r\n not included into line
position = 0;
}
else
{
reading = false;
continue;
}
if (currentArea == Area.nothing) //nothing interesting at the moment
{
//search for position of tag
position = MoveAfter("<title>", line, position); //searches until it finds <title> tag
if (position >= 0) currentArea = Area.title;
else continue;
}
(...)
if (currentArea == Area.text)
{
position = MoveAfter("<text", line, position);
if (position >= 0)
{
long index = fileIndex;
index -= line.Length;
WriteIndex(currentTitle, currentId, index);
currentArea = Area.nothing;
}
else continue;
}
}
reader.Close();
reader.Dispose();
writer.Close();
}
private void WriteIndex(string title, string id, long index)
{
writer.WriteLine(title + ";" + id + ";" + index.ToString());
}
Наилучшие пожелания и Заранее спасибо,
ventus
Править: Свяжите с дампом этой Wiki http://download.wikimedia.org/plwiki/20100629/
Ну, если это соответствует вашим требованиям, я бы сначала импортировал этот XML в RDMS, например SQL Server, а затем сделал бы запрос к этому SQL Server.
С правильными индексами (полнотекстовыми индексами, если вы хотите искать в большом количестве текста), это должно быть довольно быстро...
Это уменьшит большую часть накладных расходов, связанных с разбором XML-файла библиотеками...
Что ж ... Если вы собираетесь искать его, я настоятельно рекомендую вам найти способ получше, чем иметь дело с самим файлом. Я предлагаю, как вы упомянули, поместить его в хорошо нормализованную и проиндексированную базу данных и выполнить там поиск. Все остальное, что вы делаете, будет в точности дублировать то, что делает база данных.
Однако на это потребуется время. XmlTextReader , вероятно, ваш лучший выбор, он работает с одним узлом за раз.LINQ to XML также должен быть достаточно эффективной обработкой, но я не пробовал использовать его с большим файлом и поэтому не могу комментировать.
Могу я спросить: откуда взялся этот огромный XML-файл? Возможно, есть способ справиться с ситуацией в источнике, а не до обработки файла размером 3 ГБ.
Мне нравится идея создания индекса - вы сохраняете свой код очень простым, и вам не нужны никакие ужасные зависимости вроде баз данных :)
Итак - создайте индекс, в котором вы будете хранить следующее
[содержимое для поиска]:[смещение байта к началу xml узла, который содержит содержимое]
Чтобы получить смещение байта, вам нужно создать свой собственный поток над входным файлом, и создать читателя из него. вы будете запрашивать позицию на каждом reader.Read(...). Пример индексной записи:
"Now is the winter of our discontent":554353
Это означает, что запись в xml-файле, содержащая "Now is the winter of our discontent", находится в узле в байтовой позиции 554,353. Примечание: я бы не отказался закодировать поисковую часть индекса так, чтобы не сталкиваться с используемыми разделителями.
Чтобы прочитать индекс, вы сканируете индекс на диске (т.е. не надо загружать его в память) в поисках нужной записи. Найдя ее, вы получите смещение байта. Теперь создайте новый поток над файлом .xml и установите его позицию на смещение байта - создайте новый считыватель и читайте документ с этой точки.
вы можете сохранить файл в couchDB. Я написал для этого скрипт на Python:
import couchdb
import datetime
import time
from lxml import etree
couch = couchdb.Server()
db = couch["wiki"]
infile = '/Users/johndotnet/Downloads/plwiki-20100629-pages-articles.xml'
context = etree.iterparse(source=infile, events=("end", ), tag='{http://www.mediawiki.org/xml/export-0.4/}page')
for event, elem in context:
#dump(elem)
couchEle = {}
for ele in elem.getchildren():
if ele.tag == "{http://www.mediawiki.org/xml/export-0.4/}id":
couchEle['id'] = ele.text
if ele.tag == "{http://www.mediawiki.org/xml/export-0.4/}title":
couchEle['title'] = ele.text
if ele.tag == "{http://www.mediawiki.org/xml/export-0.4/}revision":
for subEle in ele.getchildren():
if subEle.tag == "{http://www.mediawiki.org/xml/export-0.4/}text":
couchEle['text'] = subEle.text
db[couchEle['title']] = couchEle
Он должен импортировать всю статью с идентификатором, заголовком и текстом в couchDB.
теперь вы должны ввести такой запрос:
code = '''
function(doc) {
if(doc.title.indexOf("Brzeg") > -1) {
emit(doc._id, doc);
}
}
'''
results = db.query(code)
Надеюсь, это поможет!
XmlReader будет быстрым, но вам нужно проверить, достаточно ли он быстр в вашем сценарии. Предположим, что мы ищем значение, расположенное в узле под названием Item
:
using (var reader = XmlReader.Create("data.xml"))
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element && reader.Name == "Item")
{
string value = reader.ReadElementContentAsString();
if (value == "ValueToFind")
{
// value found
break;
}
}
}
}
Единственный способ быстро выполнить поиск - это сохранить его в базе данных, как предлагали другие. Если база данных не вариант, то это, без сомнения, займет много времени. Я бы создал многопоточное приложение. Создайте рабочие потоки, которые будут читать данные и, возможно, помещать их в строковую очередь. Сделайте это как 5 потоков, сегментированных по всему файлу (так, чтобы один поток запускал начало, второй поток запускал 1/5 пути в файл, третий поток запускал 2/5 пути и т. Д. ). Между тем, у вас есть другой поток, который читает очередь строк и ищет то, что вы ищете. После этого удалите поток из очереди. Это займет некоторое время, но не должно вызывать сбоев или занимать много памяти.
Если вы обнаружите, что он потребляет много памяти, установите ограничение на количество элементов, которые может удерживать очередь, и приостановите потоки до тех пор, пока размер очереди не станет ниже этого порога.
Я бы сделал следующее:
1) Разбил XML на более мелкие файлы. Например, если XML выглядит так, то я бы создал один файл для каждого узла статьи с именем, соответствующим атрибуту title. Если название не уникально, то я бы просто пронумеровал файлы.
Поскольку файлов много, я бы разбил их на подкаталоги, в каждом из которых было бы не более 1 000 файлов.
<root>
<article title="aaa"> ... </article>
<article title="bbb"> ... </article>
<article title="ccc"> ... </article>
</root>
2) Создайте индексную таблицу с именами файлов и столбцами, по которым вы хотите искать.
3) Как вариант, вы можете хранить фрагменты XML в базе данных, а не на жестком диске. Для этого хорошо подходит тип varChar(MAX) SQL Server.
Выгрузите его в индекс Solr и используйте его для поиска. Вы можете запустить Solr как автономную поисковую систему и простой скрипт, чтобы перебрать файл и выгрузить каждую статью в индекс. Затем Solr дает вам полнотекстовый поиск по любым полям, которые вы решили проиндексировать ...