parsexml
Этот модуль реализует простой высокопроизводительный парсер XML / HTML. Поддерживается только кодировка UTF-8. Парсер разработан с некоторой коррекцией ошибок, так что даже большая часть "неправильного HTML", найденного в сети, может быть обработана им. Примечание: Этот парсер не проверяет, что каждый <tag> имеет соответствующий </tag>! Эти проверки должны быть реализованы кодом клиента по различным причинам:
- Старый HTML содержит теги, у которых нет закрывающего тега:
<br>например. - HTML-теги регистронезависимы, XML-теги регистрозависимы. Поскольку эта библиотека может анализировать оба типа, только клиент знает, какой сравнения использовать.
- Таким образом, реализовать проверки должным образом было бы очень сложно с небольшой пользой, особенно учитывая, что их легко реализовать в клиенте. Клиент должен использовать процедуру
errorMsgExpectedдля создания приятного сообщения об ошибке, которое соответствует другим сообщениям об ошибках, создаваемым этой библиотекой.
Пример 1: Извлечение заголовка HTML
Файл examples/htmltitle.nim демонстрирует, как использовать XML-парсер для выполнения простой задачи: определить заголовок HTML-документа.
# Example program to show the parsexml module
# This program reads an HTML file and writes its title to stdout.
# Errors and whitespace are ignored.
import os, streams, parsexml, strutils
if paramCount() < 1:
quit("Usage: htmltitle filename[.html]")
var filename = addFileExt(paramStr(1), "html")
var s = newFileStream(filename, fmRead)
if s == nil: quit("cannot open the file " & filename)
var x: XmlParser
open(x, s, filename)
while true:
x.next()
case x.kind
of xmlElementStart:
if cmpIgnoreCase(x.elementName, "title") == 0:
var title = ""
x.next() # skip "<title>"
while x.kind == xmlCharData:
title.add(x.charData)
x.next()
if x.kind == xmlElementEnd and cmpIgnoreCase(x.elementName, "title") == 0:
echo("Title: " & title)
quit(0) # Success!
else:
echo(x.errorMsgExpected("/title"))
of xmlEof: break # end of file reached
else: discard # ignore other events
x.close()
quit("Could not determine title!") Пример 2: Извлечение всех HTML-ссылок
Файл examples/htmlrefs.nim демонстрирует, как использовать XML-парсер для выполнения другой простой задачи: определить все ссылки, содержащиеся в HTML-документе.
# Example program to show the new parsexml module
# This program reads an HTML file and writes all its used links to stdout.
# Errors and whitespace are ignored.
import os, streams, parsexml, strutils
proc `=?=` (a, b: string): bool =
# little trick: define our own comparator that ignores case
return cmpIgnoreCase(a, b) == 0
if paramCount() < 1:
quit("Usage: htmlrefs filename[.html]")
var links = 0 # count the number of links
var filename = addFileExt(paramStr(1), "html")
var s = newFileStream(filename, fmRead)
if s == nil: quit("cannot open the file " & filename)
var x: XmlParser
open(x, s, filename)
next(x) # get first event
block mainLoop:
while true:
case x.kind
of xmlElementOpen:
# the <a href = "xyz"> tag we are interested in always has an attribute,
# thus we search for ``xmlElementOpen`` and not for ``xmlElementStart``
if x.elementName =?= "a":
x.next()
if x.kind == xmlAttribute:
if x.attrKey =?= "href":
var link = x.attrValue
inc(links)
# skip until we have an ``xmlElementClose`` event
while true:
x.next()
case x.kind
of xmlEof: break mainLoop
of xmlElementClose: break
else: discard
x.next() # skip ``xmlElementClose``
# now we have the description for the ``a`` element
var desc = ""
while x.kind == xmlCharData:
desc.add(x.charData)
x.next()
echo(desc & ": " & link)
else:
x.next()
of xmlEof: break # end of file reached
of xmlError:
echo(errorMsg(x))
x.next()
else: x.next() # skip other events
echo($links & " link(s) found!")
x.close() Импорты
- strutils, lexbase, streams, unicode, os
Типы
XmlEventKind = enum xmlError, ## an error occurred during parsing xmlEof, ## end of file reached xmlCharData, ## character data xmlWhitespace, ## whitespace has been parsed xmlComment, ## a comment has been parsed xmlPI, ## processing instruction (``<?name something ?>``) xmlElementStart, ## ``<elem>`` xmlElementEnd, ## ``</elem>`` xmlElementOpen, ## ``<elem xmlAttribute, ## ``key = "value"`` pair xmlElementClose, ## ``>`` xmlCData, ## ``<![CDATA[`` ... data ... ``]]>`` xmlEntity, ## &entity; xmlSpecial ## ``<! ... data ... >``
- перечисление всех событий, которые могут произойти при разборе Исходный код Редактировать
XmlErrorKind = enum errNone, ## no error errEndOfCDataExpected, ## ``]]>`` expected errNameExpected, ## name expected errSemicolonExpected, ## ``;`` expected errQmGtExpected, ## ``?>`` expected errGtExpected, ## ``>`` expected errEqExpected, ## ``=`` expected errQuoteExpected, ## ``"`` or ``'`` expected errEndOfCommentExpected, ## ``-->`` expected errAttributeValueExpected ## non-empty attribute value expected
- перечисление, которое перечисляет все ошибки, которые могут произойти Исходный код Редактировать
XmlParseOption = enum reportWhitespace, ## report whitespace reportComments, ## report comments allowUnquotedAttribs, ## allow unquoted attribute values (for HTML) allowEmptyAttribs ## allow empty attributes (without explicit value)
- параметры для XML-парсера Исходный код Редактировать
XmlParser = object of BaseLexer a, b, c: string kind: XmlEventKind err: XmlErrorKind state: ParserState cIsEmpty: bool filename: string options: set[XmlParseOption]
- объект парсера. Исходный код Редактировать
Процедуры
proc open(my: var XmlParser; input: Stream; filename: string; options: set[XmlParseOption] = {}) {...}{.raises: [IOError, OSError], tags: [ReadIOEffect].}- инициализирует парсер с входным потоком.
Filenameиспользуется только для удобных сообщений об ошибках. Поведение парсера можно контролировать параметромoptions: ЕслиoptionsсодержитreportWhitespace, то маркер пробела сообщается как событиеxmlWhitespace. ЕслиoptionsсодержитreportComments, то маркер комментария сообщается как событиеxmlComment. Исходный код Редактировать proc close(my: var XmlParser) {...}{.inline, raises: [Exception, IOError, OSError], tags: [WriteIOEffect].}- закрывает парсер
myи его связанный входной поток. Исходный код Редактировать proc kind(my: XmlParser): XmlEventKind {...}{.inline, raises: [], tags: [].}- возвращает текущий тип события для XML-парсера Исходный код Редактировать
proc rawData(my: var XmlParser): lent string {...}{.inline, raises: [], tags: [].}- возвращает строку 'data' по ссылке. Используется только для оптимизации. Исходный код Редактировать
proc rawData2(my: var XmlParser): lent string {...}{.inline, raises: [], tags: [].}- возвращает вторую строку 'data' по ссылке. Используется только для оптимизации. Исходный код Редактировать
proc getColumn(my: XmlParser): int {...}{.inline, raises: [], tags: [].}- получает текущий столбец, до которого добрался парсер. Исходный код Редактировать
proc getLine(my: XmlParser): int {...}{.inline, raises: [], tags: [].}- получает текущую строку, до которой добрался парсер. Исходный код Редактировать
proc getFilename(my: XmlParser): string {...}{.inline, raises: [], tags: [].}- получает имя файла, обрабатываемого парсером. Исходный код Редактировать
proc errorMsg(my: XmlParser): string {...}{.raises: [ValueError], tags: [].}- возвращает полезное сообщение об ошибке для события
xmlErrorИсходный код Редактировать proc errorMsgExpected(my: XmlParser; tag: string): string {...}{. raises: [ValueError], tags: [].}- возвращает сообщение об ошибке "<tag> expected" в том же формате, что и другие сообщения об ошибках Исходный код Редактировать
proc errorMsg(my: XmlParser; msg: string): string {...}{.raises: [ValueError], tags: [].}- возвращает сообщение об ошибке с текстом
msgв том же формате, что и другие сообщения об ошибках Исходный код Редактировать proc next(my: var XmlParser) {...}{.raises: [IOError, OSError], tags: [ReadIOEffect].}- извлекает первое/следующее событие. Это управляет парсером. Исходный код Редактировать
Шаблоны
template charData(my: XmlParser): string
- возвращает данные символов для событий:
xmlCharData,xmlWhitespace,xmlComment,xmlCData,xmlSpecial. Поднимает утверждение в отладочном режиме, еслиmy.kindне является одним из этих событий. В режиме релиза это не вызовет ошибку, но возвращаемое значение не будет действительным. Исходный код Редактировать template elementName(my: XmlParser): string
- возвращает имя элемента для событий:
xmlElementStart,xmlElementEnd,xmlElementOpen. Поднимает утверждение в отладочном режиме, еслиmy.kindне является одним из этих событий. В режиме релиза это не вызовет ошибку, но возвращаемое значение не будет действительным. Исходный код Редактировать template entityName(my: XmlParser): string
- возвращает имя сущности для события:
xmlEntity. Поднимает утверждение в отладочном режиме, еслиmy.kindне равноxmlEntity. В режиме релиза это не вызовет ошибку, но возвращаемое значение не будет действительным. Исходный код Редактировать template attrKey(my: XmlParser): string
- возвращает имя атрибута для события
xmlAttribute. Поднимает утверждение в отладочном режиме, еслиmy.kindне равноxmlAttribute. В режиме релиза это не вызовет ошибку, но возвращаемое значение не будет действительным. Исходный код Редактировать template attrValue(my: XmlParser): string
- возвращает значение атрибута для события
xmlAttribute. Поднимает утверждение в отладочном режиме, еслиmy.kindне равноxmlAttribute. В режиме релиза это не вызовет ошибку, но возвращаемое значение не будет действительным. Исходный код Редактировать template piName(my: XmlParser): string
- возвращает имя инструкции обработки для события
xmlPI. Поднимает утверждение в отладочном режиме, еслиmy.kindне равноxmlPI. В режиме релиза это не вызовет ошибку, но возвращаемое значение не будет действительным. Исходный код Редактировать template piRest(my: XmlParser): string
- возвращает остальную часть инструкции обработки для события
xmlPI. Поднимает утверждение в отладочном режиме, еслиmy.kindне равноxmlPI. В режиме релиза это не вызовет ошибку, но возвращаемое значение не будет действительным. Исходный код Редактировать
© 2006–2021 Andreas Rumpf
Licensed under the MIT License.
https://nim-lang.org/docs/parsexml.html