PC Magazine/RE logo
(С) СК Пресс 2S/95
PC Magazine, August, 1995, p. 403

Публикация баз данных в World-Wide Web

Рей Данкан


За последнее время система World-Wide Web приобрела огромную популярность как средство публикации в электронной форме мультимедиа-материалов. Нет сомнения, что сеть Web, в отличие от любого другого технологического новшества недавнего прошлого, ориентирована на отдельного человека, вызвав поистине взрывной всплеск творческой активности рядовых граждан. Однако если говорить о типичных издательствах и других фирмах в области средств массовой информации, которые не могут не знать о сети Web, то оказывается, что большинство их продолжают спокойно выпускать традиционные книги, журналы и газеты в качестве основной формы подачи информации. Этот же материал параллельно публикуется электронным способом, а дл большей привлекательности в него вводятся гиперсвязи, видеоклипы и звуковое сопровождение. Тем не менее в этом направлении еще предстоит сделать многое: создать концепцию сетевых электронных публикаций, разработать высокоразвитые инструменты для работы с ними, предложить удобные аппаратные средства отображени информации на экране и подготовить такой механизм ведения деловых операций, который оправдает все затраты.

Для того чтобы стало ясно, о чем идет речь, давайте проведем небольшой анализ: вспомните, как проходил ваш последний сеанс работы с Сетью, и прикиньте, насколько плодотворно было использовано это время. По всей видимости, вам пришлось работать на сравнительно неудобном стуле в неуютной комнате с резким освещением; изображение на экране было таким, что его пришлось пристально разглядывать, так как разрешающа способность и цветовая гамма у экрана были на порядок хуже, чем на печатной странице. Изрядную часть времени, вероятно, занимало ожидание ответа от удаленной центральной машины или переписывание больших по размеру графических файлов. Немало времени было потрачено на отказ от установленной гиперсвязи, не предоставившей полезной информации, либо на неудавшуюся попытку установить гиперсвязь, которая завершилась сообщением "host not found" (искомая главная машина не найдена). А если использовалась среда Microsoft Windows, то, вероятно, как минимум один или два раза пришлось столкнуться с "крахом" системы, потребовавшим значительного времени на ожидание ее перезагрузки и восстановление сетевого взаимодействия. В итоге лишь несколько минут из нескольких часов работы были фактически посвящены чтению ради удовольствия или получения полезной информации, прежде чем усталость или скука не завладели вами.

Много ли притягательных моментов можно найти в только что приведенном описании, чтобы в следующий раз, отдыхая на диване, в ресторанчике или на природе, отложить в сторону книгу или журнал, взяться за портативный компьютер и подключиться к сети по радиоканалу? Разве можно в такой обстановке с удовольствием и пользой заниматься изучением иностранного языка, подготовкой к экзамену по физике, размышлять о поэзии или погрузиться с головой в произведения мировой классики? Мне думается, нет! А ведь пока еще не было и речи о мотивах, побуждающих издательства отказаться от бумажной формы выпускаемой ими продукции и сконцентрироваться на возможностях Web. Учтите также, что в настоящее время нет очевидных путей получения достаточно ощутимой прибыли из такого начинания. Поэтому, пока весь мир еще только живет в ожидании появления электронной валюты, легких по весу цветных дисплеев с высокой контрастностью, разрешающей способностью 2000 точек/дюйм и 24-разрядным представлением цветов, средств широкополосной беспроводной связи пользователя с сетью, где бы он ни находился (на диване, в ресторанчике, на природе), давайте рассмотрим иные варианты использования всей удивительной инфрастуктуры World-Wide Web. Пусть, дл примера, это будет дистанционный доступ к базам данных.

Еще около года тому назад (и ранее) процесс "электронного обнародования" баз данных протекал крайне мучительно и требовал значительных затрат труда, финансовых ресурсов и фанатичной преданности начатому делу. Лишь очень немногие фирмы, уникальные или незаменимые по роду своей деятельности, такие, как Dialog и Westlaw, могли довести эту задачу до практического воплощения. Трудности получения и подготовки информации, применение конечными пользователями сравнительно низкопроизводительных методов доступа (в большинстве случаев модемной связи по телефонным каналам), а также необходимость разработки собственных пользовательских интерфейсов требовали для реализации прямого доступа к базам данных очень хорошей подготовки со стороны как обладателей информации, так и ее потребителей.

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

Стремительное распространение сети Internet, появление системы World-Wide Web, создание Web-серверов и программ просмотра Web, содержащих средства работы с формами, подготовленными на языке HTML, версия 2.0, - все это фактически устранило препятствие для разработки и сопровождения баз данных. Ныне благодар использованию Web-сервера (как буфера между базой данных и сетью) и программы просмотра Web (как внешнего интерфейса, независимого от платформы) практически каждый пользователь получает открылась возможность организации сетевой публикации баз данных, причем с обеспечением должного качества. Такие проблемы, как несовместимость старых пользовательских интерфейсов или разных платформ, сдерживающее влияние платы за лицензию по использованию базы данных или отчислений за патентованный внешний интерфейс, теперь имеют значительно меньшую остроту или вообще исчезли - благодаря доступности Web-серверов и программ просмотра созданных для любой распространенной модели центрального процессора и операционной системы.

Даже если вы являетесь лишь эпизодическим пользователем Сети, вам вероятно, приходилось использовать World-Wide Web для доступа к базам данных уже много раз. Главное в работе ряда популярных программ обслуживания каталогов Internet, например Yahoo (http::/www.yahoo.com) - это запросы к базе данных Web-сервера по URL-указателям и ключевым словам. Но поистине потрясающим примером богатства возможностей публикации баз данных в сети World-Wide Web служит проект "Геном человека" (Human Genome Project). Он был назван первым, наиболее грандиозным проектом в истории науки о жизни, и его цель состоит в том, чтобы в течение ближайших 15 лет обеспечить получение полной картины генетического кода человека. По своим масштабам и значимости его часто сравнивают с проектом "Манхэттен" (создание первой в мире атомной бомбы) эпохи Второй мировой войны. Однако в отличие от последнего, который проводился путем концентрации всех лучших физиков и математиков Америки в условиях их изоляции и давления сверху вплоть до момента окончательного достижения требуемых результатов, реализация проекта "Геном человека" происходит почти полностью децентрализованно. Финансирующие организации распределяют средства этого проекта по отдельным исследовательским группам, разбросанным по всему миру.

Несмотря на такую децентрализацию, которая, естественно, представляется барьером на пути организации эффективного взаимодействия и источником сильной раздробленности и дублирования усилий, выполнение проекта "Геном человека" происходит с опережением сроков и с экономией расходуемых денежных средств. Как этого удается достичь? Результаты, получаемые в каждом исследовательском коллективе, передаются в электронной форме по сети Internet в одну из многочисленных специализированных баз данных с учетом их содержания. Существуют реляционные базы данных по следующим направлениям: получение генных карт, определение последовательностей генов, сцепление генов, генетические зонды, генетические болезни, индивидуальные хромосомы и т. д. Затем формируютс связи между введенной информацией и родственными ей данными из той же или другой базы данных. Нова информация практически немедленно становится доступной всем, кто занимается с изучением вопросов генома человека, - с помощью Web-серверов, доступ к которым по сети Internet осуществим из любой точки земного шара.

Прежде чем двигаться дальше, давайте рассмотрим пример выполнения запроса к одной из наиболее популярных баз данных Internet по проблеме генома человека - версии для сети World-Wide Web книги Виктора Маккузика (Victor McKusick) "Наследственность человека по Менделю" (On-Line Mendelian Inheritance in Man - OMIM). Сначала мы подключаемся к серверу Университета Джона Хопкинса, где хранится база данных OMIM, и просматриваем графическую "карту", содержащую указатели на различные информационные разделы этого сервера и взаимосвязи между ними. Если щелкнуть на указателе OMIM, то перед нами появляется форма, имеющая поле ввода ключевых слов для поиска и ряд меток выбора, позволяющих сузить диапазон поиска. По щелчку на кнопке "Submit Query" (выполнить запрос) программа-сервер производит поиск в базе данных OMIM по всем ее записям и выдает упорядоченный список тем, удовлетворяющих заданным ограничениям. Наконец, если щелкнуть на любом названии в этом итоговом списке, то сервер выводит полный текст соответствующей теме. Данный текст, в свою очередь, содержит гиперсвязи, ведущие к цитируемым источникам, рисункам, видеоклипам, родственным по теме элементам, хранящимся в этой и других базах данных по вопросам генетики.

Стратегия опубликования баз данных

Можно выделить три основных подхода к опубликованию баз данных через World-Wide Web, хотя, конечно же, существуют десятки разновидностей для каждой стратегии. В целях предстоящего обсуждения будем полагать, что имеем дело со сравнительно сложной реляционной базой данных; большинство наиболее интересных по своему содержанию баз данных, опубликованных в Internet, относятся именно к этой категории.

Первая и самая простая стратегия заключается в том, чтобы разложить содержимое базы данных на отдельные логически жестко связанные выборки, или представлени (views), экспортировать их в ASCII-файлы, разметить эти файлы соответствующими управляющими кодами, или маркерами языка разметки гипертекста HTML (вручную или программно) и поместить созданные HTML-документы на Web-сервер (рис.1). Можно приложить еще немного усилий и создать одно- или многоуровневую систему индексных файлов, или оглавлений базы данных - путем ее сортировки по различным ключевым полям и последующего экспорта ключевых полей и номеров записей в ASCII-файлы для разметки маркерами HTML. Эта процедура аналогична методу, который я привел в статье о преобразовании базы данных в файл системы WinHelp (см. PC Magazine, March 14, 1995, p. 289).

Рис. 1. Схема наиболее простого способа организации публикации баз данных в World-Wide Web. Выборки (представления) из реляционной базы данных экспортируются в ASCII-файлы, размечаются маркерами HTML и помещаются на Web-сервер для обеспечени дистанционного к ним доступа. Путем экспорта выборочных данных могут создаваться одно- или многоуровневые системы индексных файлов.
________ ________ _______ ________ __________ | База | Экспорт | ASCII- | Разметка | HTML- | | Web- | HTTP | Программа| | данных |-------->| файлы |--------->| файлы |->| сервер |----->| просмотра| |________| |________| |_______| |________| | Web | |__________|

Вторая, немного более сложная, стратегия состоит в том, чтобы разложить базу данных на представления, экспортировать их в ASCII-файлы и использовать дл индексации и поиска данных программное обеспечение WAIS-сервера (Wide Area Information Server - информационный сервер территориальной, или региональной сети). Таким образом, задача поиска и извлечения нужной информации перекладывается с Web-сервера на WAIS-сервер; за первым остается лишь возобновление связи с удаленным пользователем после завершени операции поиска (рис. 2). На ранних этапах развити World-Wide Web этот метод пользовался большой популярностью при поиске информации - главным образом из-за того, что инструментальные средства WAIS тогда уже существовали и были просты в обращении (развитие концепции WAIS происходило одновременно с созданием серверов Gopher, ориентированных на текстовый режим; World-Wide Web появилась позднее).

Рис. 2. Немного более сложный вариант организации публикации баз данных в World-Wide Web по сравнению с показанным на рис. 1. Представления реляционной базы данных экспортируются в ASCII-файлы, индексируются и к ним обеспечивается доступ с помощью программного обеспечения WAIS (Wide Area Information Server).
Индексаци с помощью _____ ______ WAIS- ________ ______ _____ __________ | База|Экспорт | ASCII|утилиты | Тексто-| | WAIS-| | Web-| HTTP | Программа| | дан-|------->| файлы|------->| вая |<--| сер- |<--| сер-|<-----| просмотра| | ных | |______| | база | | вер |-->| вер |----->| Web | |_____| | данных | |______| |_____| |__________| | WAIS | |________|

Однако оба только что описанных подхода имеют несколько недостатков. Самый существенный из них заключается в том, что информация в форматах HTML или WAIS не подвергается изменениям в промежутках между последовательными операциями экспорта и, следовательно, не всегда отражает текущее состояние базы данных. Кроме того, процессы экспорта данных и разметки маркерами HTML сопряжены с немалыми временными и трудозатратами, даже если применяются лучшие методы автоматизации; это вынуждает пользователей выполнять операции экспорта реже, чем обновляется сама база данных. Еще одна проблема: при разбиении базы данных на представления в процессе экспорта в значительной мере утрачивается ее реляционная природа - если какие-либо представления не участвуют в экспорте, то они просто оказываютс недоступными для конечного пользователя.

Наконец третий, наиболее эффективный способ организации публикации баз данных заключается в том, чтобы обеспечить для конечного пользователя возможность напрямую, в режиме реального времени выполнять запросы к базе данных через специальную "шлюзовую" программу. В настоящее время для этих целей используются, как правило, Web-сервер и SQL-сервер баз данных, которые работают в высокопроизводительной среде UNIX. Web-сервер через программу просмотра Web предоставляет конечному пользователю форму, считывает с нее заданные параметры поиска и передает их через стандартизированный интерфейс CGI (Common Gateway Interface) SQL-клиенту. Программа SQL-клиента преобразует полученные параметры в соответствующее предложение SQL-запроса, направляет запрос SQL-серверу, получает от него результаты, преобразует их в HTML-файл и передает обратно на Web-сервер имя итогового файла. После этого Web-сервер пересылает HTML-файл с результатами выполненного запроса программе просмотра Web конечного пользователя (рис. 3).

Рис. 3. Наиболее сложный и эффективный способ организации публикации баз данных в Web. Параметры поиска считываются из заполняемых пользователем HTML-форм; затем они передаются "шлюзовой" программе, которая преобразует их в SQL-запрос, направляемый SQL-серверу. Получив результаты произведенного поиска, SQL-сервер возвращает их обратно "шлюзовой" программе, которая формирует из них HTML-файл и передает ею им Web-серверу. В свою очередь, Web-сервер направляет HTML-файл с результатами поиска обратно программе просмотра Web, где пользователь может с ними ознакомиться. Благодаря такой схеме программы просмотра Web могут служить независимыми от платформы внешними интерфейсами баз данных, предоставляя пользователям самую свежую на данный момент информацию.
________ ________ ________ ________ ___________ | База | | SQL- | SQL | SQL- | CGI | Web | HTTP | Программа | | данных | | сервер |<----| клиент |<----| сервер |<-----| просмотра | | |<-| базы |---->| базы |---->| |----->| Web | | | | данных | | данных | | | | | |________| |________| |________| |________| |___________|

Более подробно интерфейс CGI и "шлюзовые" программы (которых насчитывается много типов) мы рассмотрим в следующей статье. А сейчас я бы хотел показать простой пример организации публикации баз данных в Web: содержимое базы данных экспортируется в ASCII-файлы, которые затем направляются на выполняемую программным путем разметку маркерами HTML.

Практический пример

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

Оригинал, или мастер-копия, этой базы данных размещается в группе машин VAX госпиталя, доступ к которым может осуществляться через специальную программу запросов с терминалов. Для переноса данных с VAX на платформу PC, где манипулировать ими значительно легче, был произведен экспорт информации о сотрудниках в текстовый ASCII-файл, со знаками табуляции в качестве разделителей между полями; затем данные были переданы по локальной сети госпиталя в компьютер PC. Далее был произведен импорт этого файла в электронную таблицу Microsoft Excel (где неправильная кодировка отдельных символов и другие мелкие неточности могли быть легко исправлены); и наконец, файл был конвертирован в формат базы данных Microsoft Access (в Access имеетс возможность прямого преобразования данных электронной таблицы Excel в собственную таблицу базы данных).

Поскольку информация о сотрудниках была сохранена в базе данных Access, теперь с ней можно делать что угодно, включая непосредственное формирование HTML-документов. Access является исключительно мощным программным продуктом, если не считать некоторой его "прожорливости" в отношении системных ресурсов, а встроенный язык программирования Visual Basic предоставляет ему почти неограниченную гибкость. Основываясь на своем опыте создания RTF-документов, знал, что мне потребуются два типа программных модулей. В первом из них, под названием EmitHTML (лист. 1), записываются подпрограммы общего назначения, которые обеспечивают формирование таких элементов HTML-файлов, как заголовки, последние поля и "горячие" ссылки (HTML anchors - элементы привязки в формате HTML). Во втором модуле, под названием MainHTML (лист. 2), содержитс программа, отвечающая за обработку самой базы данных.

Для создания Web-версии базы данных о сотрудниках следует запустить процедуру Main() из макрокоманды. Сначала Main() вызывает подпрограмму WriteHTMLNameIndexFiles(), которая производит сортировку содержимого базы данных по фамилии и имени и создает двухуровневую систему индексных файлов, или оглавлений, упорядоченную по полному имени каждого сотрудника в базе данных. Число файлов на втором уровне такой индексной системы может достигать 26 - по одному на каждую букву английского алфавита; здесь в каждом файле содержится список тех сотрудников, фамилии которых начинаются с соответствующей буквы. В индексном файле первого уровня формируется практически полный алфавитный список букв, каждой из которых присваиваетс "горячая" ссылка на индексный файл второго уровня. На следующем этапе работы процедура Main() несколько раз обращается к подпрограмме WriteHTMLIndexFile(), котора выполняет сортировку базы данных о сотрудниках по адресу электронной почты, номеру телефона, домашнему адресу и подразделению и в каждом случае создает по одному индексному файлу первого уровня. Поскольку поиск по этим параметрам производится сравнительно редко, обычно не использую двухуровневую систему индексов, как при индексировании по полному имени.

Когда создание всех индексных файлов программой Main() завершено, она обращается к подпрограмме WriteHTMLDetailFiles() для формирования файлов с детальной информацией о сотрудниках. По каждому сотруднику заводится отдельный файл, имя которого есть функция от значения поля счетчика в записи базы данных. Использование этого принципа гарантирует наличие у каждого сотрудника уникального идентификационного номера (и таким образом, уникального имени файла), что позволяет напрямую устанавливать соответствие между сокращенными наименованиями элементов в различных индексных файлах и "индивидуальными" файлами, содержащими детальную информацию об отдельных сотрудниках. В свою очередь, WriteHTMLDetailFiles() вызывает вспомогательную подпрограмму WriteHTMLDetailItem() для формирования каждой строки "индивидуальных" файлов в формате HTML. Не будучи уверенным, что я смогу приобрести версию 3.0 языка HTML с ее возможностями вертикального выравнивания колонок в итоговой таблице, эту задачу я возложил на две подпрограммы, обеспечивающие с помощью маркера предварительного форматирования (<PRE>) обязательное использование непропорционального шрифта, а также вставку некоторого числа пробелов вместо знака табуляции.


Лист. 1. В модуле EmitHTML содержатся подпрограммы общего назначения, осуществляющие запись заголовков, последних и "горячих" ссылок в HTML-файлы.
Option Compare Database ' Сравнение строк выполняетс в том порядке, как они записаны в базе данных Option Explicit Sub EmitHTMLHeader (FileNum As Integer, FileName As String, TopicTitle As String) 'Создать заголовок HTML-файла, используя заданное им 'файла и номер файла или канала. Open FileName For OutPut As #FileNum Len = 4096 'Сначала формируются HTML-идентификаторы, название окна 'и заголовок его клиентской части. Print #FileNum, "<HTML>" Print #FileNum, "<HEAD>" Print #FileNum, "<TITLE>" & " TopicTitle & "</TITLE>" Print #FileNum, "</HEAD>" Print #FileNum, "<BODY>" Print #FileNum, "<H1>" & " TopicTitle & "</H1>" 'Ввести горизонтальную линейку, отделяющую заголовок 'клиентской части окна от дополнительной информации. Print #FileNum, "<HR>" End Sub Sub EmitHTMLHotLink (FileNum As Integer, HotLinkString As String, TargetString As String) 'Эта подпрограмма формирует "горячую" ссылку на 'указанный HTML-файл. Не переходите на новую 'строку! Переменная TargetString должна содержать 'корректный абсолютный или относительный URL-указатель. 'В переменной HotLinkString записывается текст, который 'пользователь видит на экране выделенным цветом 'или подчеркиванием. Print #FileNum, "<A HREF=" & TargetString & ">" & HotLinkString & "</A>"; End Sub Sub EmitHTMLTrailer (FileNum As Integer) 'Данная подпрограмма формирует нижний колонтитул Web 'страницы и последнее поле формата HTML; после этого 'файл закрывается. Print #FileNum, "<HR>" Print #FileNum, "<ADDRESS>" Print #FileNum, "Created" & Date$ & " / Last Modified " & Date$ & "<BR>" Print #FileNum, "Ahmanson Pediatric Center / Cedars-Sinai Medical Center / duncan@csmc.edu" Print #FileNum, "</ADDRESS>" Print #FileNum, "</BODY>" Print #FileNum, "</HTML>" Close #FileNum End Sub
Лист. 2. В модуле MainHTML записана та часть программы, которая отвечает за обработку базы данных. Процедуры WriteHTMLIndexFile() и WriteHTMLNameIndexFiles() служат для создания одно- или двухуровневой системы индексных файлов (или оглавлений), упорядоченных по заданному полю. Подпрограмма WriteHTMLDetailFiles() для каждой записи базы данных формирует отдельный файл, где содержится детальная информация, относящаяся к этой записи; имя файла есть функция значения поля счетчика записи. Процедура Main() связывают вместе все подпрограммы; она запускается с помощью макрокоманды из основного окна Access.
Rem Генератор HTML-файлов для учетного списка Rem сотрудников CSMC Rem Copyright (C) 1994-95 Ray Duncan Option Compare Database 'Сравнение строк выполняетс 'в том порядке, как они записаны 'в базе данных Option Explicit 'Объявление всех переменных 'должно осуществляться явно Function Main () 'Основная процедура программы преобразования; ее можно 'вызвать с помощью командной кнопки или путем запуска 'макрокоманды. Она создает несколько различных 'индексных файлов, отсортированных по полному имени; 'затем формирует по каждому сотруднику отдельный файл, 'содержащий детальную информацию об этом лице. Call WriteHTMLNameIndexFiles("Учетный список CSMC по полным именам сотрудников", "idxname", ".htm", "EntireName", "LastName", 1.5) Call WriteHTMLIndexFile("Учетный список CSMC по VAX- номерам", "idvax.htm", "VaxMail", "VaxMail", 1.5) Call WriteHTMLIndexFile("Учетный список CSMC по добавочным цифрам телефонных номеров", "idxext.htm", "Extension", "Extension", 1.5) Call WriteHTMLIndexFile("Учетный список CSMC по месту жительства", "idxlocn.htm", "Location", Location", 1.5) Call WriteHTMLIndexFile("Учетный список CSMC по подразделениям", "idxdept.htm", "Dept", "Department", 3) Call WriteHTMLDetailFiles("EntireName) End Function Sub WriteHTMLDetailFiles (IndexField As String) 'Данная программа генерирует HTML-текст с детальной 'информацией о сотрудниках. На каждого из них создаетс 'отдельный файл. Имя файла есть функция значения пол 'счетчика из записи по данному сотруднику. Порядок 'сортировки (который, по существу, не имеет особого 'значения) указывает вызывающая подпрограмма. Dim Dbase As Database, Dset1 As Recordset, FileName As String Dim TopicTitle As String 'Открыть базу и таблицу данных о сотрудниках. 'Задать порядок сортировки. Set Dbase = DBEngine.Workspaces(0).Databases(0) Set Dset1 = Dbase.OpenRecordset("Учетный список CSMC", DB_OPEN_TABLE) Dset1.Index = IndexField 'Теперь пролистать отсортированный список записей 'и внести детальную информацию о сотруднике. While Dset1.EOF <> True 'Сформировать название раздела и имя "индивидуального" 'файла, где будет храниться детальная информаци 'о сотруднике. При задании этого имени используетс 'значение поля счетчика. TopicTitle = (Mcase(Dset1![LastName]) & ", " & Mcase(Dset1![FirstName])) FileName = "emp" & LTrim$(Str$(Dset1![ID])) & ".htm" UpDateStatusBar("Запись файла " & FileName) 'Записать в файл с детальной информацией HTML- 'заголовок, а также полное имя данного сотрудника, 'которое будет использоваться как заголовок 'клиентской части окна. Call EmitHTMl.Header ( 1, FileName, TopicTitle) 'Ввести запрет на использование пропорциональных 'шрифтов, чтобы колонки можно было выравнивать 'по вертикали. Print #1, "<PRE>" 'Записать в файл детальную информацию о сотруднике. Call WriteHTMLDetailItem(1, "Credential", Dset1![Credential]) Call WriteHTMLDetailItem(1, "Title #1", Dset1![Title1]) Call WriteHTMLDetailItem(1, "Title #2", Dset1![Title2]) Print #1, Call WriteHTMLDetailItem(1, "Department", Dset1![Department]) Call WriteHTMLDetailItem(1, "Division", Dset1![Division]) Call WriteHTMLDetailItem(1, "Location", Dset1! [Location]) Call WriteHTMLDetailItem(1, "Mail Stop", Dset1! [MailStop]) Print #1, Call WriteHTMLDetailItem(1, "Extension", Dset1![Extension]) Call WriteHTMLDetailItem(1, "Dept. Ext.", Dset1! [DeptExt]) Call WriteHTMLDetailItem(1, "FAX", Dset1![FAX]) Call WriteHTMLDetailItem(1, "Pager", Dset1![Pager]) Call WriteHTMLDetailItem(1, "VAXMail", Dset1! [VaxMail]) Print #1, Call WriteHTMLDetailItem(1, "Other Info", Dset1![Other]) 'Снять запрет на использование пропорциональных ' шрифтов. Print #1, "</PRE>" 'Сформировать текст нижнего колонтитула и последнее ' поле формата HTML. Call EmitHTMLTrailer(1) 'Перейти к следующей записи. Dset1. MoveNext Wend 'Закрыть отсортированный список записей о сотрудниках. Dset1. Close End Sub Sub WriteHTMLDetailItem (FileNum As Integer, ItemName As String, ItemData As Variant) 'Эта небольшая, но удобная подпрограмма записывает 'заголовок поля, заменяет символы табуляции некоторым 'числом пробелов, отображает строку "индивидуального" 'файла полужирным шрифтом и ставит знак "конец строки" '(вводить <BR> или <P> не требуется, так как данна 'подпрограмма работает в пределах блока <PRE>) . Dim TabChars As integer 'Вычислить число пробелов, необходимое для замещени 'символов табуляции. TabChars = 20 - Len(ItemName) 'Если элемент имеет слишком длинное название, 'то оставить как, минимум один пробел между 'наименованием элемента и его содержимым. If TabChars < 1 Then Let TabChars = 1 'Сформировать HTML- текст для строки элемента '"индивидуального" файла, отобразить содержимое 'элемента (если оно не пусто) полужирным шрифтом. If IsNull(ItemData) Then Print #FileNum, ItemName & ":" Else Print #FileNum, ItemName & ":" & Space$(TabChars) & "<B>" & ItemData & "</B>" End If End Sub Sub WriteHTMLIndexFile (TopicTitle As String, FileName As String, IndexField As String, InfoField As String, TabStop As Single) 'Эта подпрограмма создает для базы данных сотрудников 'индексный HTML-файл, состоящий из списка гиперсвязей, 'упорядоченного по указанному полю базы данных. Dim Dbase As Database, Dset1 As Recordset Dim HotLinkString As String, TargetString As String Dim TabChars As Integer Const TabWidth = 10 'Открыть базу данных и таблицу с информацией 'о сотрудниках; задать порядок сортировки. Set Dbase = DBEngine.Workspaces(0).Databases(0) Set Dset1 = Dbase.OpenRecordset("Vчeтный список CSMC", DB_ OPEN_TABLE ) Dset1.Index = IndexField 'Создать индексный файл; записать в него 'HTML- заголовок. Call EmitHTMLHeader(1, FileName, TopicTitle) UpdateStatusBar ("Запись в файл " & FileName) 'Вставить атрибут включения предварительного 'форматирования текста. Print #1, "<PRE>" 'Теперь пролистать отсортированный список записей 'и создать индексный файл в виде списка гиперсвязей, 'ведущих к "индивидуальным" файлам с детальной 'информацией о сотрудниках. While Dset1.EOF <> True 'Сформировать имя "индивидуального" файла, использу 'значение поля счетчика в записи базы данных по этому 'сотруднику. TargetString = "emp" & LTrim$(Str$(Dset1![ID])) & ".htm" 'Пропустить записи, где поле "InfoField" не заполнено 'или не является буквенно-цифровым. If Dset1(lnfoField) >= "0" Then 'Получить из значения индексируемого поля и полного 'имени сотрудника элемент индексного файла. HotLinkString = LTrim$(RTrim$(Dset1(lnfoField))) TabChars = Int(TabWidth * TabStop) - Len ( HotLinkString ) If TabChars < 1 Then Let TabChars = 1 HotLinkString = HotLinkString & Space$(TabChars) HotLinkString = HotLinkString & Mcase(Dset1![LastName]) & ", " & Mcase(Dset1![FirstName]) 'Сформировать "горячую" ссылку 'и знак "конец строки". Call EmitHTMLHotLink(1, HotLinkString, TargetString) Print #1, End If 'Перейти к записи базы данных о следующем сотруднике. Dset1. MoveNext Wend 'Вставить атрибут отключения предварительного 'форматирования текста. Print #1, "</PRE>" 'Вставить последнее поле формата HTML и закрыть файл. Call EmitHTMLTrailer(1) 'Закрыть список записей. Dset1. Close End Sub Sub WriteHTMLNameIndexFiles (TopicTitle As String, FileName As String, Extension As String, IndexField As String, InfoField As String, TabStop As Single) 'Данная подпрограмма создает один индексный файл 1-го 'уровня и некоторое число индексных файлов 2-го уровня. 'Эти файлы служат для организации поиска фамилий 'сотрудников. Такая двухуровневая система индексации 'позволяет значительно ускорить доступ к базе данных 'по сети, особенно при использовании программ просмотра 'Web первого поколения, не реализующих многопотоковой 'обработки, например Mosaic 1.0. Dim Dbase As Database, Dset1 As Recordset Dim HotLinkString As String, TargetString As String Dim CurFirstChar As String, CurFileName As String Dim TabChars As Integer Const TabWidth = 10 CurFirstChar = "" 'Создать главный индексный файл (1-го уровня) 'по полным именам. CurFileName = FileName & Extension Call EmitHTMLHeader(2, CurFileName, TopicTitle) 'Записать в индексный файл 1-го уровня сопроводительную 'информацию; она будет отображаться над списком, 'где перечисляются "горячие" ссылки к индексным файлам '2-го уровня. Print #2, "Для просмотра списка сотрудников, фамили которых начинается с требуемой буквы, следует щелкнуть на этой букве .<P>" 'Открыть базу данных и таблицу с информацией 'о сотрудниках. Задать порядок сортировки. Set Dbase = DBEngine.Workspaces(0).Databases(0) Set Dset1 = Dbase.OpenRecordset("Учeтный список CSMC", DB_OPEN_ TABLE ) Dset1.Index = IndexField 'Теперь пролистать отсортированный список записей 'и создать индексные файлы 2-го уровня. Такие файлы 'создаются для каждой буквы алфавита, с которой 'начинается фамилия хотя бы одного сотрудника. В каждом 'индексном файле 2-го уровня содержится список 'гиперсвязей, ведущих к "индивидуальным" файлам 'сотрудников. While Dset1.EOF <> True 'Сформировать имя "индивидуального" файла, использу 'значение поля счетчика в записи базы данных по этому 'сотруднику. TargetString = "emp" & LTrim$(Str$(Dset1![ID])) & ".htm" 'Пропустить записи, где поле "InfoField" не заполнено 'или не является буквенно-цифровым. If Dset1(InfoField) >= "0" Then 'Совпадает ли первая буква в фамилии данного 'сотрудника с таковой предыдущего? If UCase$(Left$(Dset1l[LastName], 1)) <> CurFirstChar Then 'Если данная ситуация возникает не первый раз 'в текущем цикле, то закрыть обрабатываемый 'индексный файл 2-го уровня. If CurFirstChar <> "" Then 'Вставить атрибут отключения предварительного 'форматирования текста. Print #1, "</PRE>" 'Вставить последнее поле формата HTML 'и закрыть этот файл. Call EmitHTMLTrailer (1) End If 'Сформировать имя данного индексного файла 2-го 'уровня. CurFiretChar = UCase$(Left$(Dset1![LastName], 1)) CurFileName = FileName & CurFirstChar & Extension 'Ввести в индексный файл 1-го уровня ссылку, 'указывающую на рассматриваемый индексный файл '2-го уровня. Print #2, "| "; Call EmitHTMLHotLink(2, CurFirstChar & " ", CurFileName) Print #2, 'Создать индексный файл 2-го уровня и записать 'в него информацию для HTML- заголовка. Call EmitHTMLHeader(1, CurFileName, TopicTitle) UpdateStatusBar ("Запись в файл " & CurFileName) 'Вставить атрибут включения предварительного 'форматирования текста для запрета использовани 'пропорциональных шрифтов. Print #1, "<PRE>" End If 'Теперь преобразовать полное имя сотрудника 'для сохранения в индексном файле 2-го уровня. 'Если телефонный номер этого сотрудника имеет 'добавочные цифры, то это необходимо учесть. If IsNull(Dset1![Extension]) Then HotLinkString = Space$(TabWidth * Tabstop) HotLinkString = HotLinkString & Mcase(Dset1![LastName]) & ", " & Mcase(Dset1![FirstName]) Else HotLinkString = LTrim$(RTrim$(Dset1![Extension])) TabChars = Int(TabWidth * TabStop) - Len(HotLinkString) If TabChars < 1 Then Let TabChars = 1 HotLinkString = HotLinkString & Space$ (TabChars) HotLinkString = HotLinkString & Mcase(Dset1![LastName]) & ", " & Mcase(Dset1![FirstName]) End If 'Сформировать текст "горячей" ссылки и записать его 'в индексный файл 2-го уровня. Call EmitHTMLHotLink(1, HotLinkString, TargetString) Print #1, End If 'Перейти к записи базы данных о следующем сотруднике. Dset1 .MoveNext Wend 'Завершить обработку последнего индексного файла 2-го 'уровня, вставив атрибут отключения предварительного 'форматирования текста и последнее поле формата HTML. Print #1, "</PRE>" Call EmitHTMLTrailer(1) 'Завершить обработку главного индексного файла 1-го 'уровня. Print #2, "| <P>" Call EmitHTMLTrailer(2) 'Закрыть список записей. Dset1.Close End Sub