Spec-Zone.ru › Nim

std/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 std/[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 std/[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

Типы

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
перечисление, которое перечисляет все возможные ошибки Источник Редактировать
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 ... >``
перечисление всех событий, которые могут произойти при анализе Источник Редактировать
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
объект парсера. Источник Редактировать

Процедуры

proc close(my: var XmlParser) {.inline, ...raises: [IOError, OSError],
                                tags: [WriteIOEffect], forbids: [].}
закрывает парсер my и связанный с ним входной поток. Источник Редактировать
proc errorMsg(my: XmlParser): string {....raises: [ValueError], tags: [],
                                       forbids: [].}
возвращает полезное сообщение об ошибке для события xmlError Источник Редактировать
proc errorMsg(my: XmlParser; msg: string): string {....raises: [ValueError],
    tags: [], forbids: [].}
возвращает сообщение об ошибке с текстом msg в том же формате, что и другие сообщения об ошибках Источник Редактировать
proc errorMsgExpected(my: XmlParser; tag: string): string {.
    ...raises: [ValueError], tags: [], forbids: [].}
возвращает сообщение об ошибке «<tag> ожидается» в том же формате, что и другие сообщения об ошибках Источник Редактировать
proc getColumn(my: XmlParser): int {.inline, ...raises: [], tags: [], forbids: [].}
получить текущую колонку, до которой добрался парсер. Источник Редактировать
proc getFilename(my: XmlParser): string {.inline, ...raises: [], tags: [],
    forbids: [].}
получить имя файла, обрабатываемого парсером. Источник Редактировать
proc getLine(my: XmlParser): int {.inline, ...raises: [], tags: [], forbids: [].}
получить текущую строку, до которой добрался парсер. Источник Редактировать
proc kind(my: XmlParser): XmlEventKind {.inline, ...raises: [], tags: [],
    forbids: [].}
возвращает текущий тип события для XML-парсера Источник Редактировать
proc next(my: var XmlParser) {....raises: [IOError, OSError], tags: [ReadIOEffect],
                               forbids: [].}
извлекает первое/следующее событие. Это управляет парсером. Источник Редактировать
proc open(my: var XmlParser; input: Stream; filename: string;
          options: set[XmlParseOption] = {}) {....raises: [IOError, OSError],
    tags: [ReadIOEffect], forbids: [].}
инициализирует парсер с входным потоком. Filename используется только для приятных сообщений об ошибках. Поведение парсера можно контролировать с помощью параметра options: Если options содержит reportWhitespace, то маркер пробела отображается как событие xmlWhitespace. Если options содержит reportComments, то маркер комментария отображается как событие xmlComment. Источник Редактировать
proc rawData(my: var XmlParser): lent string {.inline, ...raises: [], tags: [],
    forbids: [].}
возвращает исходную строку 'data' по ссылке. Это используется только для ускорения работы. Источник Редактировать
proc rawData2(my: var XmlParser): lent string {.inline, ...raises: [], tags: [],
    forbids: [].}
возвращает исходную вторую строку 'data' по ссылке. Это используется только для ускорения работы. Источник Редактировать

Шаблоны

template attrKey(my: XmlParser): string
возвращает ключ атрибута для события xmlAttribute Вызывает утверждение в режиме отладки, если my.kind не равно xmlAttribute. В режиме релиз это не вызовет ошибку, но возвращаемое значение будет недействительным. Исходный код Изменить
template attrValue(my: XmlParser): string
возвращает значение атрибута для события xmlAttribute Вызывает утверждение в режиме отладки, если my.kind не равно xmlAttribute. В режиме релиз это не вызовет ошибку, но возвращаемое значение будет недействительным. Исходный код Изменить
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 piName(my: XmlParser): string
возвращает имя инструкции обработки для события xmlPI Вызывает утверждение в режиме отладки, если my.kind не равно xmlPI. В режиме релиз это не вызовет ошибку, но возвращаемое значение будет недействительным. Исходный код Изменить
template piRest(my: XmlParser): string
возвращает остальную часть инструкции обработки для события xmlPI Вызывает утверждение в режиме отладки, если my.kind не равно xmlPI. В режиме релиз это не вызовет ошибку, но возвращаемое значение будет недействительным. Исходный код Изменить

© 2006–2024 Andreas Rumpf
Licensed under the MIT License.
https://nim-lang.org/docs/parsexml.html

Spec-Zone.ru

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