Конвертирование HTML в форматы Microsoft Excel. Конвертирование из Excel в HTML: корректно, качественно, просто Конвертировать таблицу excel в html онлайн
Одноклассники
АЛЕКСЕЙ МИЧУРИН
Конвертирование из Excel в HTML:
корректно, качественно, просто
Итак, наша задача состоит в том, чтобы корректно конвертировать документ из формата xls в формат HTML с учётом форматирования исходного документа и при этом обойтись «малой кровью»
Постановка задачи. Или в чем проблема?
Многие веб-мастера часто сталкиваются с задачей конвертирования файлов Microsoft Excel в другие форматы. Нередко это сопряжено с трудностями, так как формат xls, как все прекрасно знают, не документирован.
В некоторых случаях возможно сохранение данных Excel в документированных форматах и последующая их обработка. Но часто этот способ работает неудовлетворительно. Простые форматы, удобные для обработки, не способны сохранить всю информацию о форматировании документа, а реализация обработчика сложных форматов неоправданно трудоёмка.
За примером далеко ходить не надо. Множество фирм, которые имеют свои веб-страницы и периодически их обновляют, ведут свои дела с использованием Excel. Перед веб-мастером при каждом обновлении информации на сервере возникает задача конвертирования. Причём задача может осложняться следующими аспектами:
Во-первых, это изменение дизайна. Price-list, подготовленный в Excel, обычно рассчитан на распечатку на чёрно-белом принтере. Price-list на веб-сайте – нет. Хотя бы поэтому простое «Сохранить как веб-страницу» не подходит (я уж молчу о том, какого качества получается HTML-код при таком сохранении).
Во-вторых, при конвертировании необходимо учесть специфику Excel. Например, очень многие люди, редактирующие price-list, широко применяют команду «Формат/строка/скрыть». При этом высота строки становится нулевой, и строка как бы исчезает с экрана и на печати. Понятно, что и на веб-сайт такие строки попасть не должны. Однако они превосходно сохраняются в других форматах и ничем не отличаются от обычных, не скрытых, строк. Это приводит к тому, что доктрина «Сохранить как текст с разделителями и обработать» не даёт удовлетворительных результатов.
В-третьих, в price-list часто используется форматирование, сохранение которого критично. Например, наименования некоторых товаров могут быть зачёркнуты или выделены цветом в знак того, что эти товары были и обязательно будут, но сейчас их нет. Некоторые позиции могут быть выделены курсивом и так далее. Вся эта информация бесследно исчезает, если пересохранить price-list в простом формате, скажем, как текст с разделителями табуляции.
Итак, наша задача состоит в том, чтобы корректно конвертировать документ из формата xls в формат HTML с учётом форматирования исходного документа и при этом обойтись «малой кровью».
Я предлагаю разбить эту задачу на две. Первая – сохранение данных в простом формате, который тем не менее будет нести в себе всю необходимую нам информацию о разметке документа. Вторая – обработка этого формата и создание HTML-страницы.
Первую задачу (экспорт) я предлагаю решить средствами Excel. Тут у нас фактически нет выбора, формат xls может обработать только то единственное на свете приложение, которое его понимает. Это диалектика.
Вторую задачу я предлагаю решить средствами языка Perl. Почему? Потому, что этот язык ориентирован на работу со строками и на решение задач, подобных нашей (Perl – Practical Extraction and Report Language – то, что надо). Потому, что этот язык знает достаточно много программистов, связанных с веб-разработками (если вы не относитесь к их числу и планируете заниматься web, то искренне рекомендую обратить внимание на Perl). Потому, что этот язык бесплатен и доступен любому пользователю на любой платформе. И потому, что мой код на Perl можно будет потом легко модифицировать, заставив его, допустим, помещать каждый раздел price-list в отдельный файл, различным образом сортировать позиции прайса, отслеживать обновления и динамику цен, снабжать каждую позицию полями HTML-форм для on-line заказа в веб-магазине... В конце концов, мой скрипт легко превратить в CGI-приложение для администрирования веб-сервера.
Такое решение представляется мне максимально гибким, функциональным и компактным, потому что каждая часть задачи решается тем средством, которое наиболее приспособлено для её решения.
Давайте оттолкнёмся от конкретного примера. В качестве «подопытного кролика» предлагаю следующий прайс (см. рис. 1).
Как видите, он сочетает в себе все неприятные элементы, упомянутые выше: и форматирование (фон, перечёркивания, жирный шрифт), и скрытые строки (если приглядеться, то можно заметить, что после девятой сразу идёт двенадцатая строка). Давайте им займёмся.
Экспорт данных из Excel
Приступим к решению первой задачи. Для экспорта данных из Excel я предлагаю несложный макрос на Visual Basic (номера строк приведены только для удобства комментирования):
1: Sub table2table()
2: "
3: " макрос, сохраняющий выделенный фрагмент таблицы
4: " в текстовом формате с отметками о форматировании
5: "
6: With ActiveWindow.RangeSelection
7: c1 = .Columns.Column
8: c2 = .Columns.Count - 1 + c1
9: r1 = .Rows.Row
10: r2 = .Rows.Count - 1 + r1
11: End With
12: If (r1 - r2 = 0 And c1 - c2 = 0) Then
13: MsgBox _
14: "что-то мало выделено (для сохранения) ,-)", _
15: vbCritical, "сообщение макроса"
16: End If
17: fileSaveName = Application.GetSaveAsFilename(_
18: InitialFileName:="file", _
19: fileFilter:="Text Files (*.txt), *.txt", _
20: Title:="сохранение страницы в нашем формате")
21: If fileSaveName = False Then
22: MsgBox _
23: "файл-то не выбран. никаких действий не предпринято.", _
24: vbCritical, "сообщение макроса"
25: Else
28: Open fileSaveName For Output As #1
29: For r = r1 To r2
30: l = CStr(Rows(r).RowHeight)
31: For c = c1 To c2
32: With Cells(r, c)
33: l = l + sep + CStr(.Text) + _
37: End With
38: Next
39: Print #1, l
40: Next
41: Close #1
42: End If
43: End Sub
Этот макрос сохраняет выделенную часть прайса в заданный файл. Макрос можно добавить к рабочей версии прайса и сделать для его вызова кнопку (вне области печати), а можно хранить в отдельном файле. Поместить его в документ очень просто: вызовите редактор Visual Basic (меню: «Сервис –> Макрос –> Редактор Visual Basic»; или
Давайте вкратце рассмотрим, как работает этот код.
Первая строка – объявление макроса. Как видите, я назвал его незамысловато table2table, вы можете наречь его более звучно.
В строках с 6 по 11 мы определяем границы выделенной части документа (ведь мы будем сохранять только выделенную часть). Теперь c1 и c2 – номера первого и последнего столбца, а r1 и r2 – первой и последней строки выделенной области.
Далее, в строках с 12 по 16 проверяем, а была ли выделена область или нашему макросу предстоит работать только с одной ячейкой. Этого, конечно, можно и не делать, но ведь, скорее всего, запускать этот макрос будете не вы, а менеджеры, редактирующие прайс, на их аккуратность не всегда можно рассчитывать. Итак, если ничего не было выделено, то наш макрос выдаст предупреждение (см. рис. 2).
В строках с 17 по 20 мы вызываем диалог Application.GetSaveAsFilename, чтобы пользователь мог выбрать имя файла (см. рис. 3).
Опять же вы можете просто задать фиксированное имя, но мне кажется, что это неудобно даже в том случае, если вы сами эксплуатируете этот макрос.
В строках с 21 по 42 следует конструкция if-then-else с проверкой, было ли указано имя файла для сохранения или пользователь нажал кнопку «Отмена» диалога «Сохранить как...».
Если пользователь отказался от сохранения, то выдаётся соответствующее сообщение (строки с 22 по 24), если имя файла указано, то начинается самое интересное – сохранение данных.
Но прежде чем обсудить процедуру сохранения (строки с 26 по 41), давайте скажем пару слов о том, в каком же именно формате мы намерены сохранять данные. Предлагаю самый простой для обработки формат: ASCII-текст. Каждая строка соответствует строке сохраняемой таблицы. Поля разделены одно символьными разделителями. Первое поле – высота строки (эта информация необходима, чтобы отфильтровать «скрытые» строки). Все последующие поля – содержимое ячеек, но каждое из этих полей содержит несколько подполей, разделённых своими разделителями. Подполя несут различную информацию о ячейке: содержание, параметры форматирования.
У нас разделители полей и подполей задаются ASCII-кодами в строках 26 и 27 соответственно. Вы можете выбрать более удобные разделители. Например, если вы уверены, что в ваших данных никогда не встречается символ «:», то можно взять его в качестве разделителя или подразделителя.
Организуем цикл по строкам (строка 29).
Для каждой строки вычисляем высоту. Заодно начинаем готовить строку для сохранения в файл в переменной l (строка 30 листинга).
В цикле (строка листинга 31) по ячейкам сохраняемой строки таблицы добавляем к строке l всю интересующую нас информацию о ячейках, снабжая её разделителями.
Какие свойства ячейки мы сохраняем?
Первым делом – текст ячейки. Обратите внимание, что мы используем именно свойство.Text, а не свойство.Value. Это не случайно. Свойство.Value возвращает истинное содержимое ячейки, свойство.Text возвращает тот текст, который отображается на мониторе и выводится на печать. Эти две величины могут не совпадать (и обычно не совпадают), потому что на экран значения выводятся согласно заданному формату ячейки (например, числа выводятся с заданным количеством знаков после запятой).
Свойство.MergeCells говорит о том, является ли ячейка частью группы объединённых ячеек.
Свойство.Font.Bold отражает жирность текста в ячейке.
Свойство.Font.Strikethrough говорит о том, был ли текст оформлен как зачёркнутый.
Для нашего примера нам, пожалуй, больше ничего не потребуется. Однако не могу не отметить ряд полезных свойств, которые могут вам пригодиться. Имена этих свойств достаточно красноречивы, и я не буду их комментировать, ограничусь перечислением:
- .Font.Name
- .Font.FontStyle
- .Font.Size
- .Font.Underline
- .Font.ColorIndex
- .Font.Italic
- .HorizontalAlignment
- .VerticalAlignment
- .ColorIndex
- .Pattern
Обратите внимание, все свойства явно приводятся к строчному типу функцией CStr (строки с 33 по 36). Это весьма полезная процедура, навсегда избавляющая вас от головной боли о преобразованиях типов.
Здесь следует сделать важную оговорку. Дело в том, что функция CStr не в состоянии обработать неопределённые значения. Если таковые будут встречаться в ваших документах, то вместо CStr вы можете использовать собственную функцию преобразования величин в текстовый формат. Например, safeCStr:
1: Function safeCStr(p As Variant) As String
2: If IsNull(p) Then safeCStr = "" Else safeCStr = CStr(p)
3: End Function
Должен отметить, что сам я никогда не сталкивался с такими ситуациями, но научно-технический консультант журнала без труда нашёл в Интернете прайс-лист, в некоторых ячейках которого свойство.Font.Bold было не определено. Я полагаю, что такие документы могут возникать в результате экспорта данных из других приложений. Например, продукты «1C» допускают экспорт данных в Excel. Одним словом, такая ситуация возможна. – Примечание автора .
Итак, вы выделяете сохраняемую область (в нашем примере это первые три столбца таблицы, строки с 4 по 21), нажимаете созданную вами кнопку, выбираете имя файла, и файл сохранён. Что с ним делать дальше?
Создание HTML-страницы по экспортированным данным
С этим файлом можно сделать всё что угодно, потому что его формат нам полностью известен (приятно это осознавать). Я приведу пример генерации HTML-страницы.
Как я и говорил, предлагаю скрипт на Perl:
1: #!/usr/bin/perl -w
3: #use strict;
5: # my ($TRUE, $FALSE)=("Истина", "Ложь");
6: my ($TRUE, $FALSE)=("True", "False");
8: sub qtnum {
9: my $t=shift;
10: $t=~s|,(\d+)|,$1|;
11: return $t;
12: }
14: sub qtstring {
15: my $t=shift;
16: $t=~s/\&/\&/g;
17: $t=~s/\"/\"/g;
18: $t=~s/\>/\>/g;
19: $t=~s/\\
20: return $t;
21: }
23: print <<"TEXT";
24:
25:
26:
27:
32:
33:
34:
| прайс некой фирмы | ||
|---|---|---|
| наименование товара | цена | |
| у.е. | руб. | |
| " .
60: $name-> . 61: " | \n";
||
|
67: TEXT 68: ($name-> eq $TRUE?" 69: qtstring($name->) . 70: ($name-> eq $TRUE?"":"") . 71: qq| | \n| .
72: ($usd-> eq $TRUE?"":"") . 73: qtnum($usd->) . 74: ($usd-> eq $TRUE?"":"") . 75: qq| | \n| .
76: ($rub-> eq $TRUE?"":"") . 77: qtnum($rub->) . 78: ($rub-> eq $TRUE?"":"") . 79: " | \n";
92:
93:


