markup::scanner - XML и HTML токенайзер
От: c-smile Канада http://terrainformatica.com
Дата: 10.09.04 05:05
Оценка: 62 (9)
Вот предлагается SGML токенайзер — markup::scanner — понимает контрукции и XML и HTML.

Для чего он может быть использован:

а) очень быстрый (наверное самый быстрый из всех возможных) и б) вообще не аллоцирует память в процессе работы.
Предлагаемый класс не включает в себя распознавние кодировок — это нужно делать на уровне istream.
Также markup::scanner не проверяет ни наименование тэгов ни атрибутов на соответсвие nmtoken правил.


Основной метод markup::scanner'а — token_type get_token() который возвращает
следующие значения:

    enum token_type 
    {
            TT_ERROR = -1,
            TT_EOF = 0,
        TT_TAG_START,   // <tag ...
                            //     ^-- happens here
        TT_TAG_END,     // </tag>
                            //       ^-- happens here 
                        // <tag ... />
                            //            ^-- or here 
        TT_ATTR,        // <tag attr="value" >      
                            //                  ^-- happens here   
         TT_WORD,        // word
        TT_SPACE        // whitespace

    };



Пример использования:
(исходный текст в следующих сообщениях)



#include "string.h"
#include "ml_scanner.h"

struct str_istream: public markup::istream
{
  const char* p;
  const char* end;

  str_istream(const char* src): p(src), end(src + strlen(src)) {}
  virtual wchar_t get_char() { return p < end? *p++: 0; }
};

int main(int argc, char* argv[])
{
  str_istream si("<html one='two' three='four' five>hello world</html>");
  markup::scanner sc(si);
  bool in_text = false;
  while(true)
  {
    int t = sc.get_token();
    switch(t)
    {
      case markup::scanner::TT_ERROR:
        printf("ERROR\n");
        break;
      case markup::scanner::TT_EOF:
        printf("EOF\n");
        goto FINISH;
      case markup::scanner::TT_TAG_START:
        printf("TAG START:%s\n", sc.get_tag_name());
        break;
      case markup::scanner::TT_TAG_END:
        printf("TAG END:%s\n", sc.get_tag_name());
        break;
      case markup::scanner::TT_ATTR:
        printf("\tATTR:%s=%S\n", sc.get_attr_name(), sc.get_value());
        break;
      case markup::scanner::TT_WORD: 
      case markup::scanner::TT_SPACE:
        printf("{%S}\n", sc.get_value());
        break;
    }
  }
FINISH:
  printf("--------------------------\n");
  return 0;
}
Re: ml_scanner.h
От: c-smile Канада http://terrainformatica.com
Дата: 10.09.04 05:09
Оценка:
//| 
//| simple XML/HTML scanner/tokenizer
//|
//| (C) Andrew Fedoniouk @ terrainformatica.com
//|

#include <wchar.h>

namespace markup 
{
  struct istream 
  {
    virtual wchar_t get_char() = 0;
  };

  class scanner
  {
  public:
    enum token_type 
    {
      TT_ERROR = -1,
      TT_EOF = 0,

        TT_TAG_START,   // <tag ...
                            //     ^-- happens here
        TT_TAG_END,     // </tag>
                            //       ^-- happens here 
                        // <tag ... />
                            //            ^-- or here 
        TT_ATTR,        // <tag attr="value" >      
                            //                  ^-- happens here   
         TT_WORD,
        TT_SPACE

    };

    enum $ { MAX_TOKEN_SIZE = 1024, MAX_NAME_SIZE = 128 };
  
  public:
  
    scanner(istream& is): 
        input(is), 
        input_char(0), 
        where(TEXT), 
        value_length(0), tag_name_length(0), attr_name_length(0) {}

    // get next token
    token_type      get_token();
    
    // get value of WORD, SPACE or ATTRibute
    const wchar_t*  get_value();
      
    // get attribute name
    const char*     get_attr_name();
    
    // get tag name
    const char*     get_tag_name();
    
    // should be overrided to resolve entities, e.g. &nbsp;
    virtual wchar_t resolve_entity(const char* buf, int buf_size) { return 0; }
        
  private: /* methods */

    token_type  scan_body();
      token_type  scan_head();

      token_type  scan_tag();
      wchar_t     skip_whitespace();
    void        push_back(wchar_t c);
  
    wchar_t     get_char();
    wchar_t     scan_entitty();

      bool        is_whitespace(wchar_t c);
      
    void        append_value(wchar_t c);
    void        append_attr_name(wchar_t c);
    void        append_tag_name(wchar_t c);

  private: /* data */

      enum state { TEXT = 0, MARKUP = 1 };
    state       where;
      token_type  token;

    wchar_t     value[MAX_TOKEN_SIZE];
    int         value_length;

    char        tag_name[MAX_NAME_SIZE];
    int         tag_name_length;

    char        attr_name[MAX_NAME_SIZE];
    int         attr_name_length;
  
    istream&    input;
    wchar_t     input_char; 

  };

}
Re[2]: ml_scanner.cpp
От: c-smile Канада http://terrainformatica.com
Дата: 10.09.04 05:13
Оценка:
//| 
//| simple XML/HTML scanner/tokenizer
//|
//| (C) Andrew Fedoniouk @ terrainformatica.com
//|

#include "ml_scanner.h"
#include <string.h>

namespace markup 
{
    scanner::token_type scanner::get_token() 
    {
        if(where == TEXT) 
            return (token = scan_body());
        else if( where == MARKUP)
            return (token = scan_head());
        return TT_ERROR;
    }

    const wchar_t* scanner::get_value() 
    {
      value[value_length] = 0;
      return value;
    }

    const char* scanner::get_attr_name() 
    {
      attr_name[attr_name_length] = 0;
      return attr_name;
    }

    const char* scanner::get_tag_name() 
    {
      tag_name[tag_name_length] = 0;
      return tag_name;
    }
        
    scanner::token_type scanner::scan_body() 
    {
        wchar_t c = get_char();

      value_length = 0;
         
      bool ws = is_whitespace(c);

      if(c == 0) return TT_EOF;
        else if(c == '<') return scan_tag();
      else if(c == '&')
          c = scan_entitty();
        
        while(true) 
        {
        append_value(c);
        c = input.get_char();
            if(c == 0)  { push_back(c); break; }
        if(c == '<') { push_back(c); break; }
        if(c == '&')
          c = scan_entitty();

            if(is_whitespace(c) != ws) 
        {
          push_back(c);
          break;
        }

        }
        return ws? TT_SPACE:TT_WORD;
    }

    scanner::token_type scanner::scan_head()
    {
        wchar_t c = skip_whitespace();

      if(c == '>') 
      {    
        where = TEXT;    
        return scan_body(); 
      }
      if(c == '/')
      {
         wchar_t t = get_char();
         if(t == '>')   { where = TEXT; return TT_TAG_END; }
         else { push_back(t); return TT_ERROR; } // erroneous situtation - standalone '/'
      }

      attr_name_length = 0;

      // attribute name...
        while(c != '=') 
        {
        if( c == 0) return TT_ERROR;
        if( c == '>' ) { push_back(c); return TT_ATTR; } // attribute without value (HTML style)
        if( is_whitespace(c) )
        {
          c = skip_whitespace();
          if(c != '=') { push_back(c); return TT_ATTR; } // attribute without value (HTML style)
          else break;
        }
            if( c == '<') return TT_ERROR;
            append_attr_name(c);
        c = get_char();
        }

      c = skip_whitespace();
      // attribute value...
      value_length = 0;
      if(c == '\"')
                while(c = get_char())
                {
                    if(c == '\"') return TT_ATTR;
            if(c == '&') c = scan_entitty();
            append_value(c);
                }
      else if(c == '\'') // allowed in html
                while(c = get_char())
                {
                    if(c == '\'') return TT_ATTR;
            if(c == '&') c = scan_entitty();
            append_value(c);
                }
      else // scan token, allowed in html: e.g. align=center
                while(c = get_char()) 
                {
                    if( is_whitespace(c) ) return TT_ATTR;
            if( c == '/' || c == '>' ) { push_back(c); return TT_ATTR; }
            if( c == '&' ) c = scan_entitty();
            append_value(c);
                }

      return TT_ERROR;
    }

    // caller already consumed '<'
    // scan header start or tag tail
    scanner::token_type scanner::scan_tag() 
    {
      tag_name_length = 0;

      wchar_t c = get_char();

        bool is_tail = c == '/';
        if(is_tail) c = get_char();
        
        while(c) 
        {
            if(is_whitespace(c)) { c = skip_whitespace(); break; }
        if(c == '/' || c == '>') break;
        append_tag_name(c);
        c = get_char();
        }
 
        if(c == 0) return TT_ERROR;    
                
        if(is_tail)
        {
            if(c == '>') return TT_TAG_END;
            return TT_ERROR;
        }
      else 
             push_back(c);
        
        where = MARKUP;
        return TT_TAG_START;
    }

    // skip whitespaces.
    // returns first non-whitespace char
    wchar_t scanner::skip_whitespace() 
    {
        while(wchar_t c = get_char()) 
        {
            if(!is_whitespace(c)) return c;
        }
        return 0;
    }

    void    scanner::push_back(wchar_t c) { input_char = c; }

    wchar_t scanner::get_char() 
    { 
      if(input_char) { wchar_t t(input_char); input_char = 0; return t; }
      return input.get_char();
    }

    // case insensitive string equality test
    // s_lowcase shall be lowercase string
    inline bool equal(const char* s, const char* s1, size_t length)
    {
      switch(length)
      {
        case 4: if(s1[3] != s[3]) return false;
        case 3: if(s1[2] != s[2]) return false;
        case 2: if(s1[1] != s[1]) return false;
        case 1: if(s1[0] != s[0]) return false;
        case 0: return true;
        default: return strncmp(s,s1,length) == 0;
      }
    }


    // caller consumed '&'
    wchar_t scanner::scan_entitty() 
    {
      char buf[32];
      int i = 0;
      wchar_t t;
      for(; i < 31 ; ++i )
      {
        buf[i] = char(t = get_char()); 
        if(t == ';')
          break;
        // || t == '<' || t == '>' || t == '\"')
      }
      buf[i] = 0;
      if(i == 2)  
      {
        if(equal(buf,"gt",2)) return '>';
        if(equal(buf,"lt",2)) return '<';
      }
      else if(i == 3 && equal(buf,"amp",3)) 
        return '&';
      else if(i == 4) 
      {
        if(equal(buf,"apos",4)) return '\'';
        if(equal(buf,"quot",4)) return '\"';
      }
      t = resolve_entity(buf,i);
      if(t) return t;
      // no luck ...
      append_value('&');
      for(int n = 0; n < i; ++n)
        append_value(buf[n]);
      return ';';
    }

    bool scanner::is_whitespace(wchar_t c)
    {
        return c <= ' ' 
            && (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f');
    }

    void scanner::append_value(wchar_t c) 
    { 
      if(value_length < (MAX_TOKEN_SIZE - 1)) 
        value[value_length++] = c;
    }

    void scanner::append_attr_name(wchar_t c)
    {
      if(attr_name_length < (MAX_NAME_SIZE - 1)) 
        attr_name[attr_name_length++] = char(c);
    }

    void scanner::append_tag_name(wchar_t c)
    {
      if(tag_name_length < (MAX_NAME_SIZE - 1)) 
        tag_name[tag_name_length++] = char(c);
    }


}
Re: markup::scanner - XML и HTML токенайзер
От: Зверёк Харьковский  
Дата: 11.09.04 02:31
Оценка:
Здравствуйте, c-smile, Вы писали:


А всякие "специальные" куски? Начинаются с "<!"
<!-- ... -->
<![CDATA[ ... ]]>

Ы?

ЗЫ: Ты письма получаешь?
FAQ — це мiй ай-кью!
Re: markup::scanner - XML и HTML токенайзер
От: c-smile Канада http://terrainformatica.com
Дата: 12.09.04 06:46
Оценка: 28 (3)
Здравствуйте, c-smile, Вы писали:

версия №2

Добавлена поддержка comment, cdata, pi и entity declaration.

ml_scanner.h:


//| 
//| simple XML/HTML scanner/tokenizer
//|
//| (C) Andrew Fedoniouk @ terrainformatica.com
//|

namespace markup 
{
  
  struct instream 
  {
    virtual wchar get_char() = 0;
  };

  typedef unsigned short wchar;

  class scanner
  {
  public:
    enum token_type 
    {
      TT_ERROR = -1,
      TT_EOF = 0,

      TT_TAG_START,   // <tag ...
                      //     ^-- happens here
      TT_TAG_END,     // </tag>
                      //       ^-- happens here 
                      // <tag ... />
                      //            ^-- or here 
      TT_ATTR,        // <tag attr="value" >      
                      //                  ^-- happens here   
      TT_WORD,
      TT_SPACE,

      TT_DATA,        // content of followings:

      TT_COMMENT_START, TT_COMMENT_END, // after "<!--" and "-->"
      TT_CDATA_START, TT_CDATA_END,     // after "<![CDATA[" and "]]>"
      TT_PI_START, TT_PI_END,           // after "<?" and "?>"
      TT_ENTITY_START, TT_ENTITY_END,   // after "<!ENTITY" and ">"
      
    };

    enum $ { MAX_TOKEN_SIZE = 1024, MAX_NAME_SIZE = 128 };

  public:
  
    scanner(instream& is): 
        input(is), 
        input_char(0), 
        c_scan(scan_body),
        value_length(0), 
        tag_name_length(0), 
        attr_name_length(0),
        got_tail(false) {}

    // get next token
    token_type      get_token() { return (this->*c_scan)(); } 
    
    // get value of TT_WORD, TT_SPACE, TT_ATTR and TT_DATA
    const wchar*    get_value();
      
    // get attribute name
    const char*     get_attr_name();
    
    // get tag name
    const char*     get_tag_name();
    
    // should be overrided to resolve entities, e.g. &nbsp;
    virtual wchar   resolve_entity(const char* buf, int buf_size) { return 0; }
        
  private: /* methods */

    typedef token_type (scanner::*scan)();
    scan        c_scan; // current 'reader'

    // content 'readers'
    token_type  scan_body();
    token_type  scan_head();
    token_type  scan_comment();
    token_type  scan_cdata();
    token_type  scan_pi();
    token_type  scan_tag();
    token_type  scan_entity_decl();

    wchar       skip_whitespace();
    void        push_back(wchar c);
  
    wchar       get_char();
    wchar       scan_entity();

    bool        is_whitespace(wchar c);
      
    void        append_value(wchar c);
    void        append_attr_name(wchar c);
    void        append_tag_name(wchar c);

  private: /* data */

    //enum state { TEXT = 0, MARKUP = 1, COMMENT = 2, CDATA = 3, PI = 4 };
    //state       where;
    token_type  token;

    wchar       value[MAX_TOKEN_SIZE];
    int         value_length;

    char        tag_name[MAX_NAME_SIZE];
    int         tag_name_length;

    char        attr_name[MAX_NAME_SIZE];
    int         attr_name_length;
  
    instream&   input;
    wchar       input_char; 

    bool        got_tail; // aux flag used in scan_comment, etc. 

  };
}



ml_scanner.cpp :

namespace markup 
{

    // case insensitive string equality test
    // s_lowcase shall be lowercase string
    inline bool equal(const char* s, const char* s1, size_t length)
    {
      switch(length)
      {
        case 8: if(s1[7] != s[7]) return false;
        case 7: if(s1[6] != s[6]) return false;
        case 6: if(s1[5] != s[5]) return false;
        case 5: if(s1[4] != s[4]) return false;
        case 4: if(s1[3] != s[3]) return false;
        case 3: if(s1[2] != s[2]) return false;
        case 2: if(s1[1] != s[1]) return false;
        case 1: if(s1[0] != s[0]) return false;
        case 0: return true;
        default: return strncmp(s,s1,length) == 0;
      }
    }

    const wchar* scanner::get_value() 
    {
      value[value_length] = 0;
      return value;
    }

    const char* scanner::get_attr_name() 
    {
      attr_name[attr_name_length] = 0;
      return attr_name;
    }

    const char* scanner::get_tag_name() 
    {
      tag_name[tag_name_length] = 0;
      return tag_name;
    }
        
    scanner::token_type scanner::scan_body() 
    {
      wchar c = get_char();

      value_length = 0;
         
      bool ws = is_whitespace(c);

      if(c == 0) return TT_EOF;
      else if(c == '<') return scan_tag();
      else if(c == '&')
         c = scan_entity();
        
      while(true) 
      {
        append_value(c);
        c = input.get_char();
        if(c == 0)  { push_back(c); break; }
        if(c == '<') { push_back(c); break; }
        if(c == '&')
          c = scan_entity();

        if(is_whitespace(c) != ws) 
        {
          push_back(c);
          break;
        }

      }
      return ws? TT_SPACE:TT_WORD;
    }

    scanner::token_type scanner::scan_head()
    {
      wchar c = skip_whitespace();

      if(c == '>') { c_scan = scan_body; return scan_body(); }
      if(c == '/')
      {
         wchar t = get_char();
         if(t == '>')   { c_scan = scan_body; return TT_TAG_END; }
         else { push_back(t); return TT_ERROR; } // erroneous situtation - standalone '/'
      }

      attr_name_length = 0;
      value_length = 0;

      // attribute name...
      while(c != '=') 
      {
        if( c == 0) return TT_ERROR;
        if( c == '>' ) { push_back(c); return TT_ATTR; } // attribute without value (HTML style)
        if( is_whitespace(c) )
        {
          c = skip_whitespace();
          if(c != '=') { push_back(c); return TT_ATTR; } // attribute without value (HTML style)
          else break;
        }
        if( c == '<') return TT_ERROR;
        append_attr_name(c);
        c = get_char();
      }

      c = skip_whitespace();
      // attribute value...
      
      if(c == '\"')
        while(c = get_char())
        {
            if(c == '\"') return TT_ATTR;
            if(c == '&') c = scan_entity();
            append_value(c);
        }
      else if(c == '\'') // allowed in html
        while(c = get_char())
        {
            if(c == '\'') return TT_ATTR;
            if(c == '&') c = scan_entity();
            append_value(c);
        }
      else // scan token, allowed in html: e.g. align=center
        while(c = get_char()) 
        {
            if( is_whitespace(c) ) return TT_ATTR;
            if( c == '/' || c == '>' ) { push_back(c); return TT_ATTR; }
            if( c == '&' ) c = scan_entity();
            append_value(c);
        }
      return TT_ERROR;
    }

    // caller already consumed '<'
    // scan header start or tag tail
    scanner::token_type scanner::scan_tag() 
    {
      tag_name_length = 0;

      wchar c = get_char();

      bool is_tail = c == '/';
      if(is_tail) c = get_char();
      
      while(c) 
      {
        if(is_whitespace(c)) { c = skip_whitespace(); break; }
        if(c == '/' || c == '>') break;
        append_tag_name(c);

        switch(tag_name_length)
        {
        case 3: 
          if(equal(tag_name,"!--",3))  { c_scan = scan_comment; return TT_COMMENT_START; }
          break;
        case 8:
          if( equal(tag_name,"![CDATA[",8) ) { c_scan = scan_cdata; return TT_CDATA_START; }
          break;
        case 7:
          if( equal(tag_name,"!ENTITY",8) ) { c_scan = scan_entity_decl; return TT_ENTITY_START; }
          break;
        }

        c = get_char();
      }
 
      if(c == 0) return TT_ERROR;    
              
      if(is_tail)
      {
          if(c == '>') return TT_TAG_END;
          return TT_ERROR;
      }
      else 
           push_back(c);
      
      c_scan = scan_head;
      return TT_TAG_START;
    }

    // skip whitespaces.
    // returns first non-whitespace char
    wchar scanner::skip_whitespace() 
    {
        while(wchar c = get_char()) 
        {
            if(!is_whitespace(c)) return c;
        }
        return 0;
    }

    void    scanner::push_back(wchar c) { input_char = c; }

    wchar scanner::get_char() 
    { 
      if(input_char) { wchar t(input_char); input_char = 0; return t; }
      return input.get_char();
    }


    // caller consumed '&'
    wchar scanner::scan_entity() 
    {
      char buf[32];
      int i = 0;
      wchar t;
      for(; i < 31 ; ++i )
      {
        buf[i] = char(t = get_char()); 
        if(t == ';')
          break;
        // || t == '<' || t == '>' || t == '\"')
      }
      buf[i] = 0;
      if(i == 2)  
      {
        if(equal(buf,"gt",2)) return '>';
        if(equal(buf,"lt",2)) return '<';
      }
      else if(i == 3 && equal(buf,"amp",3)) 
        return '&';
      else if(i == 4) 
      {
        if(equal(buf,"apos",4)) return '\'';
        if(equal(buf,"quot",4)) return '\"';
      }
      t = resolve_entity(buf,i);
      if(t) return t;
      // no luck ...
      append_value('&');
      for(int n = 0; n < i; ++n)
        append_value(buf[n]);
      return ';';
    }

    bool scanner::is_whitespace(wchar c)
    {
        return c <= ' ' 
            && (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f');
    }

    void scanner::append_value(wchar c) 
    { 
      if(value_length < (MAX_TOKEN_SIZE - 1)) 
        value[value_length++] = c;
    }

    void scanner::append_attr_name(wchar c)
    {
      if(attr_name_length < (MAX_NAME_SIZE - 1)) 
        attr_name[attr_name_length++] = char(c);
    }

    void scanner::append_tag_name(wchar c)
    {
      if(tag_name_length < (MAX_NAME_SIZE - 1)) 
        tag_name[tag_name_length++] = char(c);
    }

    scanner::token_type scanner::scan_comment()
    {
      if(got_tail)
      {
        c_scan = scan_body;
        got_tail = false;
        return TT_COMMENT_END;
      }
      for(value_length = 0; value_length < (MAX_TOKEN_SIZE - 1); ++value_length)
      {
        value[value_length] = get_char();
        if(value_length >= 2 
          && value[value_length] == '>' 
          && value[value_length - 1] == '-' 
          && value[value_length - 2] == '-')
        {
          got_tail = true;
          value_length -= 2;
          break;
        }
      }
      return TT_DATA;
    }

    scanner::token_type scanner::scan_cdata()
    {
      if(got_tail)
      {
        c_scan = scan_body;
        got_tail = false;
        return TT_CDATA_END;
      }
      for(value_length = 0; value_length < (MAX_TOKEN_SIZE - 1); ++value_length)
      {
        value[value_length] = get_char();
        if(value_length >= 2 
          && value[value_length] == '>' 
          && value[value_length - 1] == ']' 
          && value[value_length - 2] == ']')
        {
          got_tail = true;
          value_length -= 2;
          break;
        }
      }
      return TT_DATA;
    }

    scanner::token_type scanner::scan_pi()
    {
      if(got_tail)
      {
        c_scan = scan_body;
        got_tail = false;
        return TT_PI_END;
      }
      for(value_length = 0; value_length < (MAX_TOKEN_SIZE - 1); ++value_length)
      {
        value[value_length] = get_char();
        if(value_length >= 1 
          && value[value_length] == '>' 
          && value[value_length - 1] == '?')
        {
          got_tail = true;
          value_length -= 1;
          break;
        }
      }
      return TT_DATA;
    }

    scanner::token_type scanner::scan_entity_decl()
    {
      if(got_tail)
      {
        c_scan = scan_body;
        got_tail = false;
        return TT_ENTITY_END;
      }
      wchar t;
      dword tc = 0;
      for(value_length = 0; value_length < (MAX_TOKEN_SIZE - 1); ++value_length)
      {
        value[value_length] = t = get_char();
        if(t == '\"') tc++;
        else if( t == '>' && (tc & 1) == 0 )
        {
          got_tail = true;
          break;
        }
      }
      return TT_DATA;
    }
}
Re[2]: markup::scanner - XML и HTML токенайзер
От: c-smile Канада http://terrainformatica.com
Дата: 12.09.04 18:06
Оценка:
Здравствуйте, Зверёк Харьковский, Вы писали:

ЗХ>

ЗХ>А всякие "специальные" куски? Начинаются с "<!"
ЗХ><!-- ... -->
ЗХ><![CDATA[ ... ]]>

ЗХ>Ы?


Поправил уже. В стандарте еще много всякой экзотики типа %processing-entity; которые пока не распознаются.

ЗХ>ЗЫ: Ты письма получаешь?


Ага! А что? Ты посылал чего?
Если на старые email's то не дошло очевидно.
Прсьба повторить если можно.
Re[3]: markup::scanner - XML и HTML токенайзер
От: Зверёк Харьковский  
Дата: 13.09.04 10:16
Оценка:
Здравствуйте, c-smile, Вы писали:

ЗХ>>ЗЫ: Ты письма получаешь?


CS>Ага! А что? Ты посылал чего?

CS>Если на старые email's то не дошло очевидно.
CS>Прсьба повторить если можно.

Ушли два письма с подтверждением доставки на Andrew@ и info@terrainformatica.com
Ни доставки, ни подтверждения
Кстати, Алекс Мова жаловался, что он тоже пытался писать и ничего не дошло.
Может, антиспам?
FAQ — це мiй ай-кью!
Re[3]: markup::scanner - XML и HTML токенайзер
От: c-smile Канада http://terrainformatica.com
Дата: 14.09.04 03:00
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Ага! А что? Ты посылал чего?


Что ж такое делается?
Может еще кто посылал чего, а я не получил или не так понял?
(Извиняюсь за частные вопросы на публичном форуме)
Re[4]: markup::scanner - XML и HTML токенайзер
От: Виталий Россия  
Дата: 20.09.04 21:57
Оценка:
Здравствуйте, c-smile, Вы писали:

СS>Что ж такое делается?

CS>Может еще кто посылал чего, а я не получил или не так понял?
Кстати говоря я тоже письма отсылал и без ответа. Правда потом в MSN все обсудили
Re[2]: markup::scanner - XML и HTML токенайзер
От: Аноним  
Дата: 07.05.06 03:24
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Здравствуйте, c-smile, Вы писали:


CS>версия №2


Вешь замечательная, только от такого

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
<body>
  <LI> Begin &amp; back </LI>
</body>
</HTML>


крышу сносит.
При очередном парсинге возвращает(markup::scanner::TT_WORD) сначала Begin затем

0x0013d920 "& back </LI>
</body>
</HTML>
;"
Re[3]: markup::scanner - XML и HTML токенайзер
От: Аноним  
Дата: 07.05.06 03:33
Оценка:
Здравствуйте, Аноним, Вы писали:

Вот сам код, для удобства. А вешь действительно полезная

#include <stdio.h>
#include "string.h"
#include <tchar.h>
#include "ml_scanner.h"

struct str_istream: public markup::istream
{
    const TCHAR* p;
    const TCHAR* end;

    str_istream(const TCHAR* src): p(src), end(src + _tcslen(src)) {}
    virtual markup::wchar get_char() { return p < end? *p++: 0; }
};

int main(int argc, char* argv[])
{
    TCHAR t = _T('а');
    str_istream si(_T("<HTML><body><LI> Begin &amp; back </LI></body></HTML>"));
    markup::scanner sc(si);
    bool in_text = false;
    while(true)
    {
        int t = sc.get_token();
        switch(t)
        {
        case markup::scanner::TT_ERROR:
            _tprintf(_T("ERROR\n"));
            break;
        case markup::scanner::TT_EOF:
            _tprintf(_T("EOF\n"));
            goto FINISH;
        case markup::scanner::TT_TAG_START:
            _tprintf(_T("TAG START:%s\n"), sc.get_tag_name());
            break;
        case markup::scanner::TT_TAG_END:
            _tprintf(_T("TAG END:%s\n"), sc.get_tag_name());
            break;
        case markup::scanner::TT_ATTR:
            _tprintf(_T("\tATTR:%s=%s\n"), sc.get_attr_name(), sc.get_value());
            break;
        case markup::scanner::TT_WORD: 
        case markup::scanner::TT_SPACE:
            _tprintf(_T("{%s}\n"), sc.get_value());
            break;
        }
    }
FINISH:
    _tprintf(_T("--------------------------\n"));
    return 0;
}


ну и в markup — typedef TCHAR wchar;
Re[4]: markup::scanner - XML и HTML токенайзер
От: Аноним  
Дата: 07.05.06 10:46
Оценка:
Здравствуйте, Аноним, Вы писали:

Баг из-за того, что получив &amp; парсер, сохранив его в своем стеке, повторно принимает его за начало entity.
    scanner::token_type scanner::scan_body() 
    {
      wchar c = get_char(); // 2.теперь считав декодированый & принимаем его за новый entity и меленькая ж.па
      value_length = 0;
         
      bool ws = is_whitespace(c);

      if(c == 0) return TT_EOF;
      else if(c == '<') return scan_tag();
      else if(c == '&')
         c = scan_entity();
        
      while(true) 
      {
        append_value(c);
        c = input.get_char();
        if(c == 0)  { push_back(c); break; }
        if(c == '<') { push_back(c); break; }
        if(c == '&')
          c = scan_entity();

        if(is_whitespace(c) != ws) 
        {
          push_back(c); // 1.вот тут олучив из входного буфера, декодировав сохраняем в стеке
          break;
        }
      }
      return ws? TT_SPACE:TT_WORD;
    }


Править сей лаконичный код, очень "не хочется". Надеюсь c-smile молвит слово .
Пока такое решение:
-добавляем переменную класса
  bool  entity_ch;

-правим get_char
    wchar scanner::get_char() 
    { 
      if(input_char) { wchar t(input_char); if(input_char!=_T('&'))entity_ch = false; input_char = 0; return t; }
      return input.get_char();
    }

-правим scan_entity

    // caller consumed '&'
    wchar scanner::scan_entity() 
    {
      wchar buf[32];
      int i = 0;
      wchar t;
      for(; i < 31 ; ++i )
      {
        buf[i] = char(t = get_char()); 
        if(t == _T(';'))
          break;
        // || t == '<' || t == '>' || t == '\"')
      }
      buf[i] = 0;
      if(i == 2)  
      {
        if(equal(buf,_T("gt"),2)) return _T('>');
        if(equal(buf,_T("lt"),2)) return _T('<');
      }
      else if(i == 3 && equal(buf,_T("amp"),3))
      {
    entity_ch = true;
        return _T('&');
      }
      else if(i == 4) 
      {
        if(equal(buf,_T("apos"),4)) return _T('\'');
        if(equal(buf,_T("quot"),4)) return _T('\"');
      }
      t = resolve_entity(buf,i);
      if(t) return t;
      // no luck ...
      append_value(_T('&'));
      for(int n = 0; n < i; ++n)
        append_value(buf[n]);
      return ';';
    }

-правим scan_body

    scanner::token_type scanner::scan_body() 
    {
      wchar c = get_char();

      value_length = 0;
         
      bool ws = is_whitespace(c);

      if(c == 0) return TT_EOF;
      else if(c == '<') return scan_tag();
      else if(c == '&' && !entity_ch)
         c = scan_entity();
        
      while(true) 
      {
        append_value(c);
        c = input.get_char();
        if(c == 0)  { push_back(c); break; }
        if(c == '<') { push_back(c); break; }
        if(c == '&')
          c = scan_entity();

        if(is_whitespace(c) != ws) 
        {
          push_back(c);
          break;
        }
      }
      return ws? TT_SPACE:TT_WORD;
    }
Re[5]: Latest fixed version:
От: c-smile Канада http://terrainformatica.com
Дата: 10.05.06 00:00
Оценка:
Здравствуйте, Аноним, Вы писали:

.h

#ifndef __MARKUP
#define __MARKUP

//| 
//| simple XML/HTML scanner/tokenizer
//|
//| (C) Andrew Fedoniouk @ terrainformatica.com
//|

namespace markup 
{
  typedef wchar_t wchar;

  struct instream 
  {
    virtual wchar get_char() = 0;
  };


  class scanner
  {
  public:
    enum token_type 
    {
      TT_ERROR = -1,
      TT_EOF = 0,

      TT_TAG_START,   // <tag ...
                      //     ^-- happens here
      TT_TAG_END,     // </tag>
                      //       ^-- happens here 
                      // <tag ... />
                      //            ^-- or here 
      TT_ATTR,        // <tag attr="value" >      
                      //                  ^-- happens here   
      TT_WORD,
      TT_SPACE,

      TT_DATA,        // content of followings:

      TT_COMMENT_START, TT_COMMENT_END, // after "<!--" and "-->"
      TT_CDATA_START, TT_CDATA_END,     // after "<![CDATA[" and "]]>"
      TT_PI_START, TT_PI_END,           // after "<?" and "?>"
      TT_ENTITY_START, TT_ENTITY_END,   // after "<!ENTITY" and ">"
      
    };

    enum $ { MAX_TOKEN_SIZE = 1024, MAX_NAME_SIZE = 128 };

  public:
  
    scanner(instream& is): 
        input(is), 
        input_char(0), 
        value_length(0), 
        tag_name_length(0), 
        attr_name_length(0),
        got_tail(false) { c_scan = &scanner::scan_body; }

    // get next token
    token_type      get_token() { return (this->*c_scan)(); } 
    
    // get value of TT_WORD, TT_SPACE, TT_ATTR and TT_DATA
    const wchar*    get_value();
      
    // get attribute name
    const char*     get_attr_name();
    
    // get tag name
    const char*     get_tag_name();
    
    // should be overrided to resolve entities, e.g. &nbsp;
    virtual wchar   resolve_entity(const char* buf, int buf_size) { return 0; }
        
  private: /* methods */

    typedef token_type (scanner::*scan)();
    scan        c_scan; // current 'reader'

    // content 'readers'
    token_type  scan_body();
    token_type  scan_head();
    token_type  scan_comment();
    token_type  scan_cdata();
    token_type  scan_pi();
    token_type  scan_tag();
    token_type  scan_entity_decl();

    wchar       skip_whitespace();
    void        push_back(wchar c);
  
    wchar       get_char();
    wchar       scan_entity();

    bool        is_whitespace(wchar c);
      
    void        append_value(wchar c);
    void        append_attr_name(wchar c);
    void        append_tag_name(wchar c);

  private: /* data */

    //enum state { TEXT = 0, MARKUP = 1, COMMENT = 2, CDATA = 3, PI = 4 };
    //state       where;
    token_type  token;

    wchar       value[MAX_TOKEN_SIZE];
    int         value_length;

    char        tag_name[MAX_NAME_SIZE];
    int         tag_name_length;

    char        attr_name[MAX_NAME_SIZE];
    int         attr_name_length;
  
    instream&   input;
    wchar       input_char; 

    bool        got_tail; // aux flag used in scan_comment, etc. 

  };
}
 
#endif


.cpp


#include "tl_markup.h"
#include "string.h"

namespace markup 
{

    // case sensitive string equality test
    // s_lowcase shall be lowercase string
    inline bool equal(const char* s, const char* s1, size_t length)
    {
      switch(length)
      {
        case 8: if(s1[7] != s[7]) return false;
        case 7: if(s1[6] != s[6]) return false;
        case 6: if(s1[5] != s[5]) return false;
        case 5: if(s1[4] != s[4]) return false;
        case 4: if(s1[3] != s[3]) return false;
        case 3: if(s1[2] != s[2]) return false;
        case 2: if(s1[1] != s[1]) return false;
        case 1: if(s1[0] != s[0]) return false;
        case 0: return true;
        default: return strncmp(s,s1,length) == 0;
      }
    }

    const wchar* scanner::get_value() 
    {
      value[value_length] = 0;
      return value;
    }

    const char* scanner::get_attr_name() 
    {
      attr_name[attr_name_length] = 0;
      return attr_name;
    }

    const char* scanner::get_tag_name() 
    {
      tag_name[tag_name_length] = 0;
      return tag_name;
    }
        
    scanner::token_type scanner::scan_body() 
    {
      wchar c = get_char();

      value_length = 0;
         
      bool ws = is_whitespace(c);

      if(c == 0) return TT_EOF;
      else if(c == '<') return scan_tag();
      else if(c == '&')
         c = scan_entity();
        
      while(true) 
      {
        append_value(c);
        c = input.get_char();
        if(c == 0)  { push_back(c); break; }
        if(c == '<') { push_back(c); break; }
        if(c == '&')  { push_back(c); break; }
          
        if(is_whitespace(c) != ws) 
        {
          push_back(c);
          break;
        }

      }
      return ws? TT_SPACE:TT_WORD;
    }

    scanner::token_type scanner::scan_head()
    {
      wchar c = skip_whitespace();

      if(c == '>') { c_scan = &scanner::scan_body; return scan_body(); }
      if(c == '/')
      {
         wchar t = get_char();
         if(t == '>')   { c_scan = &scanner::scan_body; return TT_TAG_END; }
         else { push_back(t); return TT_ERROR; } // erroneous situtation - standalone '/'
      }

      attr_name_length = 0;
      value_length = 0;

      // attribute name...
      while(c != '=') 
      {
        if( c == 0) return TT_ERROR;
        if( c == '>' ) { push_back(c); return TT_ATTR; } // attribute without value (HTML style)
        if( is_whitespace(c) )
        {
          c = skip_whitespace();
          if(c != '=') { push_back(c); return TT_ATTR; } // attribute without value (HTML style)
          else break;
        }
        if( c == '<') return TT_ERROR;
        append_attr_name(c);
        c = get_char();
      }

      c = skip_whitespace();
      // attribute value...
      
      if(c == '\"')
        while(c = get_char())
        {
            if(c == '\"') return TT_ATTR;
            if(c == '&') c = scan_entity();
            append_value(c);
        }
      else if(c == '\'') // allowed in html
        while(c = get_char())
        {
            if(c == '\'') return TT_ATTR;
            if(c == '&') c = scan_entity();
            append_value(c);
        }
      else // scan token, allowed in html: e.g. align=center
        while(c = get_char()) 
        {
            if( is_whitespace(c) ) return TT_ATTR;
            if( c == '/' || c == '>' ) { push_back(c); return TT_ATTR; }
            if( c == '&' ) c = scan_entity();
            append_value(c);
        }
      return TT_ERROR;
    }

    // caller already consumed '<'
    // scan header start or tag tail
    scanner::token_type scanner::scan_tag() 
    {
      tag_name_length = 0;

      wchar c = get_char();

      bool is_tail = c == '/';
      if(is_tail) c = get_char();
      
      while(c) 
      {
        if(is_whitespace(c)) { c = skip_whitespace(); break; }
        if(c == '/' || c == '>') break;
        append_tag_name(c);

        switch(tag_name_length)
        {
        case 3: 
          if(equal(tag_name,"!--",3))  { c_scan = &scanner::scan_comment; return TT_COMMENT_START; }
          break;
        case 8:
          if( equal(tag_name,"![CDATA[",8) ) { c_scan = &scanner::scan_cdata; return TT_CDATA_START; }
          break;
        case 7:
          if( equal(tag_name,"!ENTITY",8) ) { c_scan = &scanner::scan_entity_decl; return TT_ENTITY_START; }
          break;
        }

        c = get_char();
      }
 
      if(c == 0) return TT_ERROR;    
              
      if(is_tail)
      {
          if(c == '>') return TT_TAG_END;
          return TT_ERROR;
      }
      else 
           push_back(c);
      
      c_scan = &scanner::scan_head;
      return TT_TAG_START;
    }

    // skip whitespaces.
    // returns first non-whitespace char
    wchar scanner::skip_whitespace() 
    {
        while(wchar c = get_char()) 
        {
            if(!is_whitespace(c)) return c;
        }
        return 0;
    }

    void    scanner::push_back(wchar c) { input_char = c; }

    wchar scanner::get_char() 
    { 
      if(input_char) { wchar t(input_char); input_char = 0; return t; }
      return input.get_char();
    }


    // caller consumed '&'
    wchar scanner::scan_entity() 
    {
      char buf[32];
      int i = 0;
      wchar t;
      for(; i < 31 ; ++i )
      {
        buf[i] = char(t = get_char()); 
        if(t == ';')
          break;
        // || t == '<' || t == '>' || t == '\"')
      }
      buf[i] = 0;
      if(i == 2)  
      {
        if(equal(buf,"gt",2)) return '>';
        if(equal(buf,"lt",2)) return '<';
      }
      else if(i == 3 && equal(buf,"amp",3)) 
        return '&';
      else if(i == 4) 
      {
        if(equal(buf,"apos",4)) return '\'';
        if(equal(buf,"quot",4)) return '\"';
      }
      t = resolve_entity(buf,i);
      if(t) return t;
      // no luck ...
      append_value('&');
      for(int n = 0; n < i; ++n)
        append_value(buf[n]);
      return ';';
    }

    bool scanner::is_whitespace(wchar c)
    {
        return c <= ' ' 
            && (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f');
    }

    void scanner::append_value(wchar c) 
    { 
      if(value_length < (MAX_TOKEN_SIZE - 1)) 
        value[value_length++] = c;
    }

    void scanner::append_attr_name(wchar c)
    {
      if(attr_name_length < (MAX_NAME_SIZE - 1)) 
        attr_name[attr_name_length++] = char(c);
    }

    void scanner::append_tag_name(wchar c)
    {
      if(tag_name_length < (MAX_NAME_SIZE - 1)) 
        tag_name[tag_name_length++] = char(c);
    }

    scanner::token_type scanner::scan_comment()
    {
      if(got_tail)
      {
        c_scan = &scanner::scan_body;
        got_tail = false;
        return TT_COMMENT_END;
      }
      for(value_length = 0; value_length < (MAX_TOKEN_SIZE - 1); ++value_length)
      {
        value[value_length] = get_char();
        if(value_length >= 2 
          && value[value_length] == '>' 
          && value[value_length - 1] == '-' 
          && value[value_length - 2] == '-')
        {
          got_tail = true;
          value_length -= 2;
          break;
        }
      }
      return TT_DATA;
    }

    scanner::token_type scanner::scan_cdata()
    {
      if(got_tail)
      {
        c_scan = &scanner::scan_body;
        got_tail = false;
        return TT_CDATA_END;
      }
      for(value_length = 0; value_length < (MAX_TOKEN_SIZE - 1); ++value_length)
      {
        value[value_length] = get_char();
        if(value_length >= 2 
          && value[value_length] == '>' 
          && value[value_length - 1] == ']' 
          && value[value_length - 2] == ']')
        {
          got_tail = true;
          value_length -= 2;
          break;
        }
      }
      return TT_DATA;
    }

    scanner::token_type scanner::scan_pi()
    {
      if(got_tail)
      {
        c_scan = &scanner::scan_body;
        got_tail = false;
        return TT_PI_END;
      }
      for(value_length = 0; value_length < (MAX_TOKEN_SIZE - 1); ++value_length)
      {
        value[value_length] = get_char();
        if(value_length >= 1 
          && value[value_length] == '>' 
          && value[value_length - 1] == '?')
        {
          got_tail = true;
          value_length -= 1;
          break;
        }
      }
      return TT_DATA;
    }

    scanner::token_type scanner::scan_entity_decl()
    {
      if(got_tail)
      {
        c_scan = &scanner::scan_body;
        got_tail = false;
        return TT_ENTITY_END;
      }
      wchar t;
      unsigned int tc = 0;
      for(value_length = 0; value_length < (MAX_TOKEN_SIZE - 1); ++value_length)
      {
        value[value_length] = t = get_char();
        if(t == '\"') tc++;
        else if( t == '>' && (tc & 1) == 0 )
        {
          got_tail = true;
          break;
        }
      }
      return TT_DATA;
    }
}
Re: markup::scanner - XML и HTML токенайзер
От: c-smile Канада http://terrainformatica.com
Дата: 11.05.06 06:11
Оценка:
Последняя версия:
http://www.terrainformatica.com/codelib/view.php?sid=8
Re[6]: Latest fixed version:
От: Аноним  
Дата: 11.05.06 23:46
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Здравствуйте, Аноним, Вы писали:


Как всё просто оказалось
Re[2]: markup::scanner - XML и HTML токенайзер
От: Аноним  
Дата: 11.05.06 23:48
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Последняя версия:

CS>http://www.terrainformatica.com/codelib/view.php?sid=8

А ето, там ченжлога нет ?
И чем отличается от этой
Автор: c-smile
Дата: 10.05.06
?
Re[3]: markup::scanner - XML и HTML токенайзер
От: c-smile Канада http://terrainformatica.com
Дата: 12.05.06 03:41
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, c-smile, Вы писали:


CS>>Последняя версия:

CS>>http://www.terrainformatica.com/codelib/view.php?sid=8

А>А ето, там ченжлога нет ?


Потому что codelib это "даренный конь" — он же GNU в прстонародье.

А>И чем отличается от этой
Автор: c-smile
Дата: 10.05.06
?




  scanner::token_type scanner::scan_body() 
    {
      wchar c = get_char();

      value_length = 0;
         
      bool ws = false;

      if(c == 0) return TT_EOF;
      else if(c == '<') return scan_tag();
      else if(c == '&')
         c = scan_entity();
      else
         ws = is_whitespace(c);
        
      while(true) 
      {
        append_value(c);
        c = input.get_char();
        if(c == 0)  { push_back(c); break; }
        if(c == '<') { push_back(c); break; }
        if(c == '&') { push_back(c); break; }
          
        if(is_whitespace(c) != ws) 
        {
          push_back(c);
          break;
        }

      }
      return ws? TT_SPACE:TT_WORD;
    }


я выделил места которые были изменены.
Re[2]: markup::scanner - XML и HTML токенайзер
От: Аноним  
Дата: 17.07.06 15:30
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Последняя версия:

CS>http://www.terrainformatica.com/codelib/view.php?sid=8
Ссылка не работает. Можно поправить?
Re[3]: markup::scanner - XML и HTML токенайзер
От: WinterMute Россия http://yarrr.ru
Дата: 18.07.06 12:49
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, c-smile, Вы писали:


CS>>Последняя версия:

CS>>http://www.terrainformatica.com/codelib/view.php?sid=8
А>Ссылка не работает. Можно поправить?

Посмотри здесь: http://www.codeproject.com/cpp/HTML_XML_Scanner.asp
Re[4]: markup::scanner - XML и HTML токенайзер
От: Alny Украина  
Дата: 19.07.06 08:45
Оценка:
CS>>>Последняя версия:
CS>>>http://www.terrainformatica.com/codelib/view.php?sid=8
А>>Ссылка не работает. Можно поправить?

WM>Посмотри здесь: http://www.codeproject.com/cpp/HTML_XML_Scanner.asp


а по http://www.terrainformatica.com/codelib/view.php?sid=8 по прежнему

Scriptorium could not connect to the database. The reason was: DB Error: no such database

Локальные проблемы?
Просто по адресу http://www.codeproject.com/cpp/HTML_XML_Scanner.asp насколько я понимаю, далеко не последняя версия. И судя по коментариям, последние изменения именно на оф. сайте?
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.