Файл dsl. Первый словарь на языке DSL. II. Главный этап: получение текста словарных статей

DSL (Dictionary Specification Language) – язык, разработанный компанией ABBYY для разметки словарных статей в соответствии с технологией показа словарей, принятой в Lingvo. Компилятор языка DSL входит в состав ABBYY Lingvo начиная с 6 версии. Таким образом, любой пользователь словаря может создать и распространять свой собственный словарь.

Основное назначение языка DSL - описание того, как показывать словарную статью. Язык не определяет в точности структуру карточки или возможные типы информации. Например, в языке DSL нет понятия "синоним", "лексическое значение" или "синтаксическая модель", но есть понятия "курсив", "ссылка", "подстатья" и т.д.

Словарь может быть создан прямо на языке DSL в виде простого текстового файла. Этот файл следует сохранить в кодировке ANSI или Unicode, а затем изменить расширение файла на dsl.

Пример содержимого исходного файла и соответствующей ему карточки в Lingvo:

Словарный [p][i][c]прил. от словарь lexical; lexicographic [*]богатый словарный запас - copious vocabulary [*]- словарный состав

Создание DSL-словарей

Рассмотрим создание DSL-словаря на примере марийско-русского словаря на основе Словаря марийского языка в 10-томах (1990-2005), изданного «Марийским научно-исследовательским институтом языка, литературы и истории имени В.М. Васильева» (МарНИИЯЛИ). Пользователю, работающему в Unix-подобных системах (Linux, Mac OS и т.д.), удобнее работать с текстами в UTF-8, текстовый редактор может быть любым, например, gedit . После того, как закончили редактировать текст будущего dsl-словаря, например, marirus.txt , сохраните его с расширением .dsl в кодировке UTF-16 :

Синтаксис DSL

  • ударение - ["],
  • цветной текст - ,
  • полужирный текст - [b],
  • курсив - [i],
  • подчёркивание - [u],
  • условные сокращения - [p],
  • исключить из индексации - [!trs],
  • зона перевода - ,
  • неосновной текст - ,
  • язык - ,
  • отступ:

- , - отступ на 1 пункт; - , - отступ на 2 пункта; - , - отступ на 3 пункта; ... - , - отступ на 9 пунктов

  • гиперссылка - ,
  • медиафайлы (mp3, jpg и т.д.) - [s],
  • комментарии - ,

Файл аннотации

В Abbyy Lingvo аннотации к словарю записываются в файле: *.ann marirus.dsl , то файл аннотации должен называться marirus.ann . При сохранении данного файла не забудьте, также, указать кодировку как UTF-16 . Файл аннотации примерно может выглядеть вот так:

«Словарь марийского языка» (10 томов), Йошкар-Ола, Марийское книжное издательство, 1990-2005: ... Программирование: Пирогов М.С. - [email protected], Чемышев А.В. - [email protected] © МарНИИЯЛИ, АУ «Марий Эл Радио», ИП Очеев В.Я., 2011 Перевод в формат Abbyy Lingvo - Центр инновационных языковых технологий при КРАГСиУ http://komikyv.ru/ http://fu-lab.ru/

Файл условных сокращений

Условные сокращения и их расшифровка записываются в файле *_abrv.dsl , например, если у нас основной файл marirus.dsl , то файл условных сокращений должен называться marirus_abrv.dsl . При сохранении данного файла не забудьте, как и в предыдущих случаях, указать кодировку UTF-16 . Файл условных сокращений может выглядеть следующим образом: marirus_abrv.dsl

Использование dsl-словарей в GoldenDict

Итак, марийско-русский словарь на основе 10-томного «Словаря марийского языка» в формате DSL готов:

http://mari-lab.ru/downloads/marirus.zip

В таком виде его можно использовать в GoldenDict, подробно об этом описано в статье

Использование созданных словарей в Abbyy Lingvo

Если собираетесь использовать этот или другой dsl-словарь в Abbyy Lingvo, то его необходимо скомпилировать специальной программой, которая входит в комплект Abbyy Lingvo.

При наличии на компьютере установленной антивирусной программы можносканировать все файлы на компьютере, а также каждый файл в отдельности . Можно выполнить сканирование любого файла, щелкнув правой кнопкой мыши на файл и выбрав соответствующую опцию для выполнения проверки файла на наличие вирусов.

Например, на данном рисунке выделен файл my-file.dsl , далее необходимо щелкнуть правой кнопкой мыши по этому файлу, и в меню файла выбрать опцию «сканировать с помощью AVG» . При выборе данного параметра откроется AVG Antivirus, который выполнит проверку данного файла на наличие вирусов.


Иногда ошибка может возникнуть в результате неверной установки программного обеспечения , что может быть связано с проблемой, возникшей в процессе установки. Это может помешать вашей операционной системе связать ваш файл DSL с правильным прикладным программным средством , оказывая влияние на так называемые «ассоциации расширений файлов» .

Иногда простая переустановка ABBYY Lingvo Dictionary может решить вашу проблему, правильно связав DSL с ABBYY Lingvo Dictionary. В других случаях проблемы с файловыми ассоциациями могут возникнуть в результате плохого программирования программного обеспечения разработчиком, и вам может потребоваться связаться с разработчиком для получения дополнительной помощи.


Совет: Попробуйте обновить ABBYY Lingvo Dictionary до последней версии, чтобы убедиться, что установлены последние исправления и обновления.


Это может показаться слишком очевидным, но зачастую непосредственно сам файл DSL может являться причиной проблемы . Если вы получили файл через вложение электронной почты или загрузили его с веб-сайта, и процесс загрузки был прерван (например, отключение питания или по другой причине), файл может повредиться . Если возможно, попробуйте получить новую копию файла DSL и попытайтесь открыть его снова.


Осторожно: Поврежденный файл может повлечь за собой возникновение сопутствующего ущерба предыдущей или уже существующей вредоносной программы на вашем ПК, поэтому очень важно, чтобы на вашем компьютере постоянно работал обновленный антивирус.


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

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


Совет: Если при попытке открыть файл DSL вы получаете сообщение об ошибке, связанной с.SYS file , проблема, вероятно, может быть связана с поврежденными или устаревшими драйверами устройств , которые необходимо обновить. Данный процесс можно облегчить посредством использования программного обеспечения для обновления драйверов, такого как DriverDoc .


Если шаги не решили проблему , и у вас все еще возникают проблемы с открытием файлов DSL, это может быть связано с отсутствием доступных системных ресурсов . Для некоторых версий файлов DSL могут потребоваться значительный объем ресурсов (например, память/ОЗУ, вычислительная мощность) для надлежащего открытия на вашем компьютере. Такая проблема встречается достаточно часто, если вы используете достаточно старое компьютерное аппаратное обеспечение и одновременно гораздо более новую операционную систему.

Такая проблема может возникнуть, когда компьютеру трудно справиться с заданием, так как операционная система (и другие службы, работающие в фоновом режиме) могут потреблять слишком много ресурсов для открытия файла DSL . Попробуйте закрыть все приложения на вашем ПК, прежде чем открывать Lingvo Dictionary Data File. Освободив все доступные ресурсы на вашем компьютере вы обеспечите налучшие условия для попытки открыть файл DSL.


Если вы выполнили все описанные выше шаги , а ваш файл DSL по-прежнему не открывается, может потребоваться выполнить обновление оборудования . В большинстве случаев, даже при использовании старых версий оборудования, вычислительная мощность может по-прежнему быть более чем достаточной для большинства пользовательских приложений (если вы не выполняете много ресурсоемкой работы процессора, такой как 3D-рендеринг, финансовое/научное моделирование или интенсивная мультимедийная работа). Таким образом, вполне вероятно, что вашему компьютеру не хватает необходимого объема памяти (чаще называемой «ОЗУ», или оперативной памятью) для выполнения задачи открытия файла.

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

Одна из самых известных и плодотворных групп давно уже работает на сайте forum.ru-board.com. Со временем там накопилась как обширнейшая коллекция словарей, так и основательнейшая база знаний и инструментов в помощь их создателям и редакторам. Было написано множество скриптов и программ, набор которых отражает историю и изменения популярности языков программирования, более или менее приспособленных для обработки текста. Тут и Perl с Python, и языки пакетных файлов для оболочек, и макросы MS Word и Excel, и компилируемые программы на языках общего назначения.

Однако до последнего времени один из языков почти не был представлен в данной сфере. Хотелось бы восполнить этот пробел и отдать должное стремительному росту мощности, функциональности и популярности языка JavaScript. Думается, он может оказать большую помощь современным программистам-лексикографам, особенно на границе сетевой и локальной лексикографии.

Создание локальной копии сетевого словаря проходит обычно несколько стадий: сохранение HTML-страничек при помощи программ вроде Teleport, очистка их от тегов при помощи регулярных выражений (в текстовых редакторах, макросами или скриптами), окончательная разметка их языком DSL. JavaScript в его Node.js варианте позволяет существенно сократить и облегчить этот путь, ведь этот язык родной для WEB и умеет оперировать сетевыми данными, не опускаясь до шаткого и изменчивого уровня кода и регулярных выражений, но работая на уровне элементов DOM .

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

Если же вы пока не планируете сохранять какой-то сетевой словарь, можете заглянуть сразу в третью часть статьи: там собраны примеры других частых задач при работе с электронными словарями, которые можно решить при помощи Node.js.

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

Подразумевается, что читатель знает JavaScript в его чистом и прикладном вариантах и самостоятельно разобрался в основах Node.js. Если это не так, придётся начинать с азов или восполнять пробелы: JavaScript , DOM и Node.js .

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

Поскольку мы будем запускать скрипты только на своих компьютерах и при этом использовать новые возможности языка, рекомендуется установка последней версии Node.js.

I. Предварительный этап: получение списка адресов словарных статей

Возможны как минимум три алгоритма создания локальной копии сетевого словаря.

1. В самом худшем случае словарь не предоставляет никакого надёжного механизма для перебора всех статей. Тогда приходится анализировать шаблон адресов и подставлять в него подряд слова из какого-нибудь более-менее полного списка лексем данного языка (можно позаимствовать набор вокабул из самого большого оцифрованного толкового словаря), отбраковывая неудачные запросы.

2. Некоторые словари позволяют идти по цепочке от первой вокабулы до последней (через ссылку «следующее слово» или набор ссылок на ближайшие вокабулы). Это самый простой способ, но не самый наглядный: трудно будет предварительно оценить общее количество вокабул, а затем следить за прогрессом копирования. Поэтому, хотя тот же Urban Dictionary предоставляет эту возможность (на страничке каждого слова есть столбец ссылок на ближайшие предыдущие и последующие статьи), мы будем пользоваться третьим способом.

3. Если словарь имеет отдельный список ссылок на все словарные статьи, мы предварительно копируем весь набор этих ссылок в файл. Так мы получаем представление о предстоящем объёме запросов и возможность следить за процентом сделанного. Например, в Urban Dictionary по ссылкам типа www.urbandictionary.com/browse.php?character=A , www.urbandictionary.com/browse.php?character=A&page=2 и т.д. можно получить список адресов всех статей с вокабулами на указанную букву (типа www.urbandictionary.com/define.php?term=a , www.urbandictionary.com/define.php?term=a%5E_%5E и т.д.).

Итак, весь процесс сохранения словаря будет разделён на два этапа, за каждый будет отвечать отдельный скрипт.

Вот код первого, сохраняющего список ссылок на словарные статьи:

1. В начальной части скрипта мы загружаем необходимые библиотечные модули (или сразу необходимые методы из них, если они будут вызываться часто). Почти все модули встроенные, устанавливающиеся вместе с инсталляцией Node.js. Из внешних нам понадобится только модуль jsdom : сам по себе Node.js не умеет анализировать HTML-страницы и превращать их в DOM-дерево, и эту способность нам обеспечит упомянутый модуль (установка модулей проста, ведь вместе с Node.js инсталлируется менеджер модулей npm; просто откройте консоль, перейдите в папку с созданным скриптом и наберите npm install jsdom , а затем дождитесь окончания скачивания и установки - сам модуль и необходимые ему подчинённые модули будут установлены в папку node_modules , там их и будет искать наш скрипт).

После загрузки модулей скрипт определяет папку, в которую будет сохранять файлы (если пользователь не задал папку при запуске скрипта первым ключом командной строки, будет выбрана папка, в которой находится скрипт), и создаёт в ней три будущих документа: сам список адресов словарных статей; список обработанных страниц, с которых он будет брать эти адреса; отчёт о произошедших ошибках.

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

Массив английского алфавита (для поочерёдного добавления букв при создании URL страниц со ссылками; последним в массив добавляется символ *, отвечающий за список ссылок на вокабулы, начинающиеся со спецсимволов);
- предыдущий и текущий URL запросов (чтобы при ошибках определять, запрашиваем ли мы всё тот же злополучный адрес, или же ошибка касается нового адреса и должна быть внесена в отчёт);
- флаг пользовательского прерывания работы скрипта.

2. Во второй части мы устанавливаем обработчики для двух событий: для любого окончания работы скрипта (там мы закрываем все файлы и вызываем функцию, которая звуком привлекает внимание пользователя к любому важному событию) и для команды пользователя прервать работу программы (вызывается нажатием клавиш Ctrl+C и переключает флаг прерывания, который проверяется перед каждым новым сетевым запросом).

3. В третьей части мы запускаем цикл запросов, в котором будем получать и сохранять списки адресов словарных статей. Часть делится на два логических блока.

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

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

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

4. Для звуковой сигнализации в служебной функции playAlert() я выбрал консольный кросс-платформенный плеер из библиотеки ffmpeg (ключи запуска см. на сайте разработчиков), но можно воспользоваться любым другим плеером или понравившимся модулем генерации звука системными средствами. Звук тоже можно выбрать любой другой.

5. Функция getDoc(url) отправляет запрос на получение очередной страницы со списком адресов словарных статей. Сперва она проверяет, не требовал ли пользователь прервать работу скрипта (скрипт работает несколько часов, поэтому может возникнуть необходимость в перерыве). Затем функция обновляет переменные прошлого и предстоящего запроса. Наконец, она даёт команду модулю jsdom запросить страницу, одновременно передавая соответствующему методу функцию, которую нужно будет вызвать с получением страницы.

В коде закомментированы две дополнительные возможности.

А. Если вы планируете запускать несколько скриптов параллельно для ускорения закачки, лучше делать это через анонимизирующий прокси-сервер. Я тестировал связку Fiddler + Tor (в небраузерном варианте из Expert Bundle) - хоть и не использовал её на всём протяжении работы скрипта, поскольку она одновременно замедляет скорость общения с сервером одного процесса, и мне не хотелось усложнять работу разделением задания на части для параллельных процессов. Пример реализации .

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

Б. Другая предосторожность от забанивания со стороны сервера - использовать задержки между запросами. Достаточно обернуть вызов метода в setTimeout и поэкспериментировать с размером пауз. Мой опыт показал, что серверам Urban Dictionary достаточно естественных пауз между запросами, никаких дополнительных перерывов вставлять не требуется.

6. Функцию processDoc(err, window) вызывает модуль jsdom , получив страницу или наткнувшись на ошибку, - отсюда два соответствующих аргумента функции.

Сначала функция проверяет аргумент err: если он определён, значит, запрос был неудачным. В таком случае скрипт сигнализирует звуком, потом записывает сообщение в файл ошибок (если это первая ошибка с данным URL, а не очередная в цепочке повторных запросов), выводит информацию в окно и заголовок консоли и перезапускает запрос, обращаясь к функции getDoc(url) с повтором аргумента.

Если же аргумент err пуст, функция начинает анализировать полученный документ. Тут возможно несколько результатов и реакций на них.

а. На странице есть порция ссылок на словарные статьи. Тогда функция записывает список адресов этих ссылок в файл содержания словаря, записывает адрес текущей страницы в файл обработанных страниц, сообщает в консоль количество сохранённых ссылок и пытается найти URL следующей страницы с адресами. Если поиск успешен, программа выдаёт в консоль информацию о следующем запросе (букву и номер страницы) и отправляет его уже знакомой нам функции getDoc(url) . Если поиск неудачен, программа проверяет алфавитный массив: если в нём остались буквы, она переходит к новой, если он пуст - завершает работу.

б. Если ссылок на странице нет, но адрес совпадает с запрошенным, значит, скорее всего, произошла ошибка не сервере (так бывает, например, когда сервер даёт ответ о временной недоступности). В таком случае скрипт повторяет запрос.

в. Если ссылок нет и адрес не совпадает с запрошенным, значит, произошло перенаправление. Такое возможно из-за одной особенности списка адресов словарных статей на Urban Dictionary: иногда предполагаемое количество страниц с этим списком на текущую букву бывает больше реального количества страниц, и при попытке запросить несуществующий номер страницы в конце буквенного блока сервер переадресует пользователя на главную страницу. В таком случае скрипт переходит к следующей букве, если алфавитный массив не пуст.

г. Если массив пуст, скрипт завершает работу.

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

II. Главный этап: получение текста словарных статей

Структура второго скрипта будет похожа, различия в основном будут происходить из существенного роста как времени работы (оно теперь будет измеряться не часами, а днями), так и сложности обработки страниц:

1. В первой части мы снова загружаем почти те же модули, затем проверяем уже два ключа запуска программы: первым задаётся пусть к папке с входящим файлом (в ней скрипт будет искать список ссылок на словарные статьи, сохранённый на предыдущем этапе), затем путь к папке для новых исходящих файлов. В обоих случаях, если ключи не заданы, используется папка самого скрипта. Скрипт проверяет наличие файла со ссылками - если его не окажется, программа завершит работу с соответствующим объявлением.

Регулярное выражение для форматирования больших чисел и число миллисекунд в часе - они будут регулярно использоваться в дальнейшем;
- контейнеры для постоянного или временного хранения данных (списка ссылок на словарные статьи, списка заголовков текущей статьи и списка её разделов - разных пользовательских интерпретаций вокабулы);
- уже знакомые нам переменные для предыдущего и предстоящего запросов;
- переменные для вычисления и вывода скорости работы скрипта;
- флаг пользовательского прерывания работы.

2. Вторая часть вводит уже описанные выше обработчики событий: завершения работы скрипта и команды пользователя прервать работу.

3. В третьей части мы проверяем по размеру основного выходного файла, первый ли это запуск программы или очередной после перерыва. Если работа только начинается, мы вписываем в файл будущего словаря метку BOM и начальные директивы формата DSL.

4. Четвёртая часть завершает процедурный раздел программы. В ней мы сначала считываем наш входной файл со списком ссылок на словарные статьи в контейнер, который будет определять дальнейшие запросы (он будет похож на алфавитный контейнер из первого скрипта - станет отправной точкой нашего цикла запросов). Затем, как и в предыдущем скрипте, мы проверяем файл отчёта об уже обработанных адресах: если в нём что-то есть, мы находим конечную строчку, в котором записана последняя успешно сохранённая статья словаря, сокращаем соответственно массив адресов для будущей обработки, запоминаем количество оставшейся работы и запускаем функцию, которая раз в час будет высчитывать скорость работы скрипта и приблизительно предсказывать время окончания работы (в гипотетическом случае её непрерывности). Затем выводим в консоль количество оставшихся адресов (это большое число, поэтому мы разделим его разряды пробелами для лучшей читаемости) и запускаем наш привычный цикл запроса и сохранения страниц. Если файл отчёта пуст, мы пропускаем его чтение, переходя сразу ко второй части перечисленных действий.

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

5. Функция playAlert() ничем не отличается от одноимённой из первого скрипта.

6. Функцией secure(str, isHeadword) мы будем регулярно пользоваться при сохранении словарных статей в файл DSL. У неё две задачи: перевести встречающиеся, как ни странно, в сетевом тексте управляющие символы (символы из начального блока ASCII) в условно читаемый вид, который не будет смущать компилятор DSL; и сократить слишком длинные слова из статей, выходящие за граничные требования формата DSL (заголовки будут сокращаться по другим правилам).

7. Функция setSpeedInfo() будет работать параллельно основному течению программы. Раз в час она будет подменять информационную строку, в которой отображается скорость работы скрипта и оставшееся время (в начале работы в строке будут сплошные знаки вопроса, которые после первого часа заменятся на числа). Работа функции довольно прозрачна, следует лишь сделать два примечания: в переменной restMark хранится количество оставшихся адресов на время предыдущего вычисления скорости; запуск звуковой сигнализации о пересчёте скорости совершается в этой функции асинхронно (то есть скрипт не ждёт конца звучания) - для этого вначале мы сохранили в переменной отдельный метод асинхронного запуска дочерних процессов.

8. Функция getDoc(url) , отправляющая сетевой запрос, ничем не отличается от описанной в предыдущем разделе, включая закомментированные предосторожности от забанивания сервером и способы ускорения работы.

9. Функция processDoc(err, window) , по сравнению с одноимённой из предыдущего скрипта, сохранит каркас, но будет существенно отличаться в части обработки и сохранения информации с полученной страницы - ведь нам придётся не просто записать набор ссылок, но проанализировать и преобразовать целый блок данных.

Впрочем, начало функции не претерпело изменений: мы всё так же проверяем аргумент err и, если он определён, заносим информацию в файл отчёта об ошибках и перезапускаем неудавшийся запрос.

Если же пока ошибок нет, мы начинаем анализировать страницу. Возможны следующие повороты событий.

а. На странице находится ожидаемая словарная статья, адрес страницы не даёт оснований подозревать переадресацию.

В таком случае мы превращаем в массив все части словарной статьи, то есть все пользовательские интерпретации слова. Затем переходим к анализу каждого элемента.

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

Все заголовки мы будем накапливать в особом буфере (чтобы избежать добавления дубликатов, мы будем использовать новую для JavaScript структуру данных Set , сохраняющую лишь уникальные элементы). Перед этим каждый заголовок мы будем прогонять через упомянутую функцию secure(str, isHeadword) , затем создавать два варианта: заголовок для заголовочной части DSL и заголовок для помещения в самое начало раздела карточки DSL - ведь к этим областям предъявляются разные требования. В каждом варианте мы будем экранировать требуемые символы. Первый вариант перед помещением в буфер мы сократим согласно требованиям формата, если он будет слишком длинным.

Поскольку для извлечения текста из DOM-элементов модуль jsdom пользуется свойством textContent , имеющим ряд недостатков, мы дополнительно перестраховываемся от потери переводов строк, дополнительно вставляя их символьные варианты перед некоторыми тегами br .

Затем мы последовательно обрабатываем части толкования и примеров перед сохранением их во временные переменные: удаляем пробельные символы в начале и конце строк, сокращаем повторяющиеся пробелы, экранируем требуемые спецсимволы, вставляем требуемые для тела карточки начальные отступы, перестраховываемся от потери пустых разделительных строк во время будущей компиляции DSL.

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

В конце мы объединяем все части в очередной элемент буфера, накапливающего части словарной статьи.

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

б. Ожидаемой статьи нет, но адрес не говорит о переадресации. Для этого могут быть как минимум две причины.

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

Если смайлика нет, возможна ошибка на сервере. В этом случае скрипт перезапускает запрос того же адреса.

в. Адрес страницы отличается от ожидаемого шаблона - произошла переадресация.

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

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

III. Дальнейшие операции с полученным словарём

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

1. Смена кодировки

Компилятор ABBYY Lingvo требует кодировки UTF-16, а GoldenDict может работать и с кодировкой UTF-8 (которая для словарей на чистой латинице даёт в два раза меньший размер файла). Некоторые текстовые редакторы с разной скоростью обрабатывают большие файлы в этих двух кодировках - это тоже может стать причиной конвертации. Конечно, можно пересохранить файл в другой кодировке при помощи редактора, но это не всегда оказывается самым быстрым и удобным путём.

Node.js предоставляет простые способы конвертации больших файлов, быстрые и нетребовательные к памяти. Вот два примера для упомянутых кодировок (к имени нового файла перед расширением прибавляется метка кодировки):

2. Замены текста

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

Приведу три примера с возрастающей сложностью.

а. Простая замена

Данный скрипт появился в ответ на просьбу одного из пользователей сделать разделы примеров из Urban Dictionary скрываемыми - то есть обернуть их в теги вторичного отображения. Написание скрипта и создание запрошенной версии словаря заняло несколько минут.

Для других простых случаев достаточно заменить в этом файле строчки с регулярными выражениями.

б. Удаление метки BOM из начала файла

Необходимость в таком действии возникла, когда я заметил, что PDF-принтер превращает BOM в пробел при создании PDF файла. Хотя бывают и другие случаи, когда метка BOM сбивает программы с толку (особенно в кодировке UTF-8).

в. Сложные замены

В следующем скрипте приводится пример замен, поставленных в зависимость от разных условий.

Разные замены срабатывают в зависимости от принадлежности строчки к директивам DSL, к заголовку статьи или к её телу.

Из директив убирается строчка с ключевым словом #ICON_FILE , вставляемая порой при декомпиляции LSD-словарей (даже тогда, когда своей встроенной иконки у словаря нет).

Слова из заголовков переводятся в нижний регистр, если их нет в массиве исключений. Такая необходимость иногда возникает, если все символы заголовков словаря даны в верхнем регистре. Массив можно наполнить именами собственными и аббревиатурами, можно хранить его в отдельном файле и загружать в начале работы скрипта.

В тегах отступа из тела карточек число отступов увеличивается на единицу ( превращается в и т.д.).

Можно использовать этот скрипт как шаблон для других замен - нужно лишь закомментировать невостребованные строки и подредактировать необходимые.

Во всех примерах этого подраздела кодировка входящего и исходящего файлов задана в коде (примеры других возможных кодировок закомментированы для возможной замены). Но можно задавать её и в ключе запуска скрипта, немного переделав код. Можно добавить и два ключа, если кодировка старого и нового файла должна будет различаться.

3. Подсчёт элементов

Следующим скриптом я считал количество элементов в готовом словаре Urban Dictionary, а именно число заголовков, карточек, интерпретаций внутри всех карточек и строк в файле.

4. Извлечение элементов

Следующим скриптом можно извлечь все заголовки из словаря DSL (например, для построения списка слов языка по самым большим словарям или для формирования списка исключений для описанного выше скрипта, переводящего заголовки в нижний регистр).

5. Проверка уникальности элементов

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

6. Конвертация DSL в текстовый формат

Читабельный текстовый вариант словаря, помимо самостоятельного значения, может стать основой для конвертации в форматы PDF и DjVu. Это обуславливает особенности выбранной структуры текстового файла, в частности его жёсткое форматирование - задание полей пробельными символами и однозначный перенос строк в границах абзаца.

Начало скрипта традиционное. Добавляются два новых модуля:

Скрипт принимает два обязательных аргумента: путь к файлу и количество строк на страницу (которое он уменьшает на два, чтобы оставить место номеру страницы после пустой строки).

Для более удобного форматирования номеров мы загрузим модуль string , предоставляющий множество функций для работы со строчными данными. В частности, нам понадобится функция, помещающая строку посередине ряда пробелов заданной ширины, и функция, удаляющая правую часть этого ряда посте вставки. Таким образом мы сможем поместить номера посередине строки внизу страницы.

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

8. Разделение файла на части.

Тут мы приведём два примера разделений, зависящие от разных параметров.

а. Разделение по буквам алфавита

Иногда с большим словарём легче работать, если разделить его на алфавитные части. При этом, например, Adobe Acrobat умеет воспринимать все части как одно целое, если проиндексировать целиком их родительскую папку и потом осуществлять поиск по индексу всего её содержимого.

Перед началом работы скрипт создаёт дополнительный файл отчёта, в котором будет сохранять информацию о размере частей (о количестве строчек на файл с отдельной буквой). Это может представлять интерес как теоретический (статистика), так и практический (представление о времени последующей обработки частей). Впрочем, это факультативное усложнение.

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

Процедурная часть начинается с вызова функции newPart(chr) , которая будет запускаться каждый раз, как нужно будет создавать файл для хранения очередной части. Эта функция:

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

После создания файла для первой части следует цикл чтения целого словаря: каждая строка проверяется упомянутым регулярным выражением, при необходимости создаётся новый файл; в текущий файл записывается текст очередной части, счётчик строк для отчётного файла увеличивается; в конце цикла записывается размер последней части и все файлы закрываются.

Этот скрипт может также пригодиться для разделения огромных файлов DSL, которые оказываются не по зубам компилятору ABBYY (например, в недавних версиях Lingvo при попытках компиляции словарей «Мультитран» из самого объёмного русско-английского направления компилятор вылетал с сообщением о нехватке памяти). В таком случае может понадобиться разделение словаря всего на две части, по средней букве алфавита (поэтому упомянутый выше алфавитный массив можно сократить до одной этой буквы).

б. Разделение по количеству страниц

Когда я пытался напечатать в PDF весь словарь целиком из текстового редактора для больших файлов, то наткнулся на ограничение: редактор отправлял на принтер не более 65000 страниц. Пришлось делить словарь на куски по 65000 страниц, печатать их по отдельности, а потом объединять части в Adobe Acrobat. Для этого и был написан следующий скрипт.

Скрипт напоминает предыдущий за некоторыми исключениями:

Скрипт принимает три обязательных аргумента при запуске: путь к целому словарю, количество строк на одну страницу, количество страниц на одну часть разделённого словаря;
- файл отчёта не создаётся, потому что размер частей будет относительно одинаковым;
- функция создания новой части упрощается (нет записи в файл отчёта и создания проверочного регулярного выражения), в название части вместо буквы алфавита вносится номер первой страницы в составе общей нумерации;
- цикл чтения/записи вызывает создание нового файла, основываясь не на первых буквах заголовков словарных частей, но на счётчиках строк и страниц.

Источники словарей для GoldenDict

Программа может работать с множеством существующих словарей. Здесь мы перечислим некоторые основные их источники.

Предупреждение: здесь на этом сайте не содержатся файлы словарей, за исключением пары общеизвестных, которые вероятно не нарушают никакие из авторских прав.Скачивая какой-либо из словарей упомянутый тут, или на какой-либо другой странице этого сайта, вы сами принимаете на себя ответственность за возможные нарушения авторских прав. К сожалению, по большей части существующие словари не являются бесплатным, и эта область, как известно, весьма туманна. Поэтому: ИСПОЛЬЗУЙТЕ ЛЮБЫЕ РАЗМЕЩЕННЫЕ ЗДЕСЬ ССЫЛКИ НА СВОЙ СТРАХ И РИСК Мы рекомендуем покупать словари, поощряя таким образом их создателей Покупайте, а затем отбрасывайте их бестолковые программные оболочки, вместо этого используя легально купленнное содержание с GoldenDict.

Словари Babylon (.bgl)

Поддерживаются и содержащиеся в.bgl-файлах изображения и ресурсы.

  • http://www.babylon.com - официальный сайт Babylon
  • http://www.babylon.com/gloss/glossaries.php - бесплатные словари, глоссарии и энциклопедии.

Если вместо.bgl файлов скачиваются.exe-инсталляторы, то используйте чтобы распаковать их (внутри должны быть файлы.bgl). Используйте полную версию 7-Zip поддерживающую cab-файлы.

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

Словари StarDict (.ifo/.dict/.idx/.syn)

  • - официальный сайт StarDict.
  • - страница со словарями

Словари в формате Dictd (.index/.dict [.dz])

  • http://www.dict.org/ - официальный сайт
  • - A collection of free dictionaries in DICT format at the StarDict home page
  • ftp://ftp.aioe.org/Dictd/ - Aioe.org hosts a large collection of free ready to use DICT dictionaries and a free DICT server

ABBYY Lingvo .dsl-словари

Поддерживаются исходные файлы словарей - .dsl, вместе с сокращениями. Опционально файлы могут быть сжаты с помощью dictzip . Ресурсы словаря (картинки, произношение слов) могут быть упакованы в.zip файл. Например, если словарь называется Longman.dsl, то ресурсы следует упаковать в Longman.dsl.files.zip и положить рядом со словарем.

  • http://www.lingvo.ru/ - официальный сайт
  • http://lingvodics.com/ - Все словари для ABBYY Lingvo® - это самый полный каталог словарей, разговорников и энциклопедий в формате ABBYY Lingvo с указанием сайтов, с которых их можно скачать. Каталог обновляется и пополняется регулярно!
    • http://lingvodics.com/pages/sites/ - большой список сайтов со словарями в формате ABBYY Lingvo.
  • http://traduko.lib.ru/ - Словари для Lingvo. Здесь содержится довольно много хороших словарей, все доступны для загрузки.
  • http://www.lingvoda.ru/ - Ассоциация лексикографов Lingvo - сайт, посвященный созданию словарей Lingvo.
    • Готовые словари можно скачать отсюда: http://www.lingvoda.ru/dictionaries/index.asp
  • http://forum.ru-board.com/topic.cgi?forum=93&topic=2972 - тема на форуме Ru-board.com , целиком посвященная созданию пользовательских словарей в.dsl-формате. Участники очень активны, и есть масса уже готовых словарей. Однако, чтобы попасть на форум, необходима регистрация (свободная).

Некоторые словари Lingvo доступны только в бинарном.lsd-формате. Это тот же.dsl, но в скомпилированной форме. Нет официального способа для распаковки их обратно в.dsl, но некоторые неофициальные способы существуют. Если вам жизненно необходимо сконвертировать некоторые.lsd обратно в.dsl, вы можете поискать решение на вышеупомнутом форуме, либо просто погуглить.

Наша официальная позиция по декомпиляции официальных словарей Lingvo заключается в следующем: мы считаем, что если вы легально владеете копией программы Lingvo, то вы в праве использовать ваши словари в любой другой программе по вашему выбору, и поэтому вам должно быть позволено декомпилировать эти словари, для того чтобы привести их в удобную форму. Однако это действие может быть запрещена явным образом в пользовательском лицензионном соглашении Lingvo (EULA). Насколько это легально - вопрос спорный. Проконсультируйтесь с юристом, если у вас есть сомнения.

Звуковые словари произношений

ABBYY Lingvo аудио архивы (.lsa/.dat)

Можно ссылаться на них из.dsl файлов, либо использовать независимо (необходима индексация)

Произвольные наборы звуковых файлов

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

If you are interested in English, Russian, German, Spanish, French, Italian or Portuguese lookups, you can download better morphologies for those languages. They contain more stems and therefore work better for dictionary lookups. Do not replace your system myspell dictionaries with them - they don"t add any new words, just more stems. Just put them to a separate directory and change morphology path in the program to point to it. Note that Windows installers already contain some of those files, but source packages don"t. The "En-Ru-En content" package above also includes them. -->

Примечание для владельцев авторских прав

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