1

libxml2 の sax インターフェイスを使用して xml ファイルを解析しようとしています。うまく機能することもありますが、xml で 2 行の順序を変更すると (有効なままです)、解析後に一部の値が無効になります。startElement に startElementNsSAX2Func を使用しています。現在の要素の属性を格納する引数 const xmlChar ** attributes があります。

startElement メソッドの開始時に、属性を処理する単純なオブジェクトを作成します。クラスのコードは次のとおりです。

class XMLElementAttributes {
public:
  static const int AttributeArrayWidth = 5;
  static const int LocalNameIndex = 0;
  static const int PrefixIndex = 1;
  static const int URIIndex = 2;
  static const int ValueIndex = 3;
  static const int EndIndex = 4;

  XMLElementAttributes( int nb_attributes, const xmlChar **attributes) :
  nb_attributes(nb_attributes),
  attributes(attributes){
  }

  xmlChar* getLocalName( int index ) const {
    return (xmlChar*)attributes[ AttributeArrayWidth * index + LocalNameIndex];
  }

  xmlChar* getValue( int index ) const{
      return (xmlChar*)std::string(attributes[ AttributeArrayWidth * index + ValueIndex],attributes[ AttributeArrayWidth * index + EndIndex]).c_str(); 
  }

  int getLength() const{
    return nb_attributes;
  }

private:
  int nb_attributes;
  const xmlChar ** attributes;
};

(xmlChar は Typedef unsigned char xmlChar です)

次に、属性の値を保存する必要がある場合は、この staic メソッドを使用してクローンを作成します (libxml2 の xmlStrdup も使用しようとしましたが、結果は同じです)。

xmlChar* cloneXMLString(const xmlChar* const source) {
    xmlChar* result;
    int len=0;
    std::cout<<"source"<<std::endl;
    while (source[len] != '\0'){
        std::cout<<(void*)&source[len] << ": " << source[len] <<std::endl;
        len++;
    }
    std::cout<<std::endl;
    std::cout<<"result, "<<std::endl;
    result = new xmlChar[len+1];
    for (int i=0; i<len; i++){
        result[i] = source[i];
        std::cout<<(void *)&source[i] << ": "<< source[i] << std::endl;
    }
    std::cout<<std::endl;
    result[len] = '\0';
    return result;
}

99% で動作しますが、最後の結果にソースと同様のものが含まれない場合があります。出力例を次に示します (入力は abcdef で、\0 で終了します)。

source
0x7fdb7402cde8: a
0x7fdb7402cde9: b
0x7fdb7402cdea: c
0x7fdb7402cdeb: d
0x7fdb7402cdec: e
0x7fdb7402cded: f


result, 
0x7fdb7402cde8: !
0x7fdb7402cde9: 
0x7fdb7402cdea: 
0x7fdb7402cdeb: 
0x7fdb7402cdec: x
0x7fdb7402cded: 

私はそれを次のように呼んでいます:

xmlChar* value = cloneXMLString(attributes.getValue(index));

したがって、ソースのアドレスは変更されていませんが、値は変更されています。xml ファイルの解析は問題なく続行され、複製後の次の値が再び有効になります。

xml ファイルが変更されていない場合、エラーは常に同じ要素と引数にあります。たとえば、xml で何かを少し変更すると、次のようになります。

<somenodes a="arg1" b="arg2">
  <node c="abc" d="def" />
  <node c="ghi" d="jkl" />
</somenodes>

<somenodes a="arg1" b="arg2">
  <node c="ghi" d="jkl" />
  <node c="abc" d="def" />
</somenodes>

エラーが別の場所に表示されるか、エラーが消えて解析が正常に機能します。何が原因でしょうか?

編集:

私の開始要素メソッド:

void MyParser::startElement( void * ctx,
        const xmlChar * localName,
        const xmlChar * prefix,
        const xmlChar * URI,
        int nb_namespaces,
        const xmlChar ** namespaces,
        int nb_attributes,
        int nb_defaulted,
        const xmlChar ** attrs ){

    XMLElementAttributes attributes ( nb_attributes, attrs );

    switch ( state ) {
    case Somestate:
       if ( xmlStrcmp( localName, StrN("SomeName").xmlCharForm() ) == 0) {
         someVar = new SomeObject(attributes);
       } 
    break;

    ...

    }
}

StrN は char* から xmlChar を作成します。someVar は MyParser クラスの静的フィールドです (startElement も静的です)。SomeObject のコンストラクターで、次のような属性の値を取得しようとします。

class SomeObject {
    public:
    SomeObject( XMLElementAttributes &attributes){
        for (int i=0; i< attributes.getLength(); i++) {
            xmlChar* name = attributes.getLocalName(i);
            if ( xmlStrcmp( name, StrN("somename").xmlCharForm()) == 0 ) {
                somename = cloneXMLString(attributes.getValue(i));
            }
            ...
        }
    }
};
4

1 に答える 1

0

ソースが有効なメモリを指していないことは明らかです。これは、メモリが既に解放されているか、既に終了した関数で宣言されたスタック メモリを指していたことが原因である可能性があります。

このようなメモリは、予測できない方法で上書きされる可能性があります。これは、ここで見られるものです。

cloneXMLStringより詳細な回答を得るには、特に呼び出し方法と、この関数に渡すメモリの発生場所など、より多くのコンテキストを確認する必要があります。

于 2012-10-30T13:16:18.313 に答える