Spec-Zone.ru › Nim 1

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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API