【问题标题】:XML QDomElement comparison made general - C++/QtXML QDomElement 比较通用 - C++/Qt
【发布时间】:2012-11-22 15:42:58
【问题描述】:

我实现了一个例程 C++/Qt 来比较大多数情况下的 QDomElement。

bool XMLtools::compare( QDomElement & element1, QDomElement & element2 )
{
    QString tag1 = element1.tagName() ;   
    QString tag2 = element2.tagName() ;
    if ( tag1 != tag2 )
        return false ;
    QList<QDomElement> elts1 = getChildElements(element1);
    QList<QDomElement> elts2 = getChildElements(element2);
    QDomElement c1, c2, tmp ;

    QString name1, name_tmp, text1, text2 ;
    if(elts1.size() != elts2.size())
        return false ;
    if(elts1.size() == 0)
    {
        text1 = c1.text() ;
        text2 = c2.text() ;

        if( text1 != text2 )
            return false ;
    }
    for ( int i = elts1.size() - 1 ; i > -1 ; i-- ) 
    {
        c1 = elts1.at(i);
        QString name1 = c1.tagName();

        for( int j = elts2.size() - 1 ; j > -1 ; j-- )
        {
            tmp = elts2.at(j) ;
            name_tmp = tmp.tagName() ;

            if( name_tmp == name1 )
            {
                c2 = tmp ;
                break ;
            }

            if( j == 0 )
                return false ; 
        }
        if ( ! compare(c1, c2) )
            return false ;      
    }
    return true ;
}

1。是否可以使用此方法比较所有 QDomNode 元素(即比较 text())?

  1. 特别是,我应该谨慎对待特殊情况,如QDomCDATASection 元素和二进制格式?

  2. 如果QDomNode包含二进制数据,如何比较它们,当内部数据相同时返回true?

谢谢!

【问题讨论】:

  • 即使是相当 comles xml 与自身的比较也会因您的代码而失败。考虑让 2 个元素具有相同的标签和不同的内容:xml: &lt;a&gt;x&lt;/a&gt;&lt;a&gt;y&lt;/a&gt;; xml2: &lt;a&gt;y&lt;/a&gt;&lt;a&gt;x&lt;/a&gt;
  • @Lol4t0 回答工作人员

标签: c++ xml qt parsing binary


【解决方案1】:

我想出了以下相等性检查算法:

两个节点ab相等,如果a &gt;= bb&gt;=a

bool compare( QDomElement  element1, QDomElement  element2 )
{
    return ! lessThen(element1, element2) && !lessThen(element2, element1);
}

现在我们只需要介绍

比较算法

现在将使用原生的QString比较功能

首先,在一般方法中,我们不仅要比较 QDomElements,还要比较 Dom 中的每个 QDomNode

  • 如果a.nodeType &lt; b.nodeType,那么a &lt; b
  • 否则,如果a.nodeName &lt; b.nodeName,那么a &lt; b
  • 否则,如果a.children.size() &lt; b.children().size(),那么a &lt; b
  • 否则,如果a.children().size() ==0 &amp;&amp; b.children().size() ==0,则比较nodeValue()。 请注意,这种方法适用于属性节点和文本节点。 QDomElement 本身没有nodeValue。如果是一些文本,写在元素内,元素将有文本类型的子节点。这就是为什么element.nodeValue() 调用总是返回空字符串的原因。
  • 否则,使用lessThen比较函数对给定节点的所有子节点进行排序。
  • 然后,检查c 属于ad 属于b 的每个孩子:
    • 如果d &gt; c,那么a &gt; b
    • 如果c &lt; d,那么a &lt; b
    • 否则 (c==d) 继续比较下一个 cd
  • 如果所有c都等于d,那么a == b,显然a &lt; b是错误的。

代码,实现这个算法:

QList<QDomNode> getChildElements(const QDomNode& e)
{
    QList<QDomNode> r;
    for (int k = 0; k < e.childNodes().size(); ++k) {
        QDomNode n = e.childNodes().at(k);
        r << n;
    }
    return r;
}


bool lessThen( QDomNode  element1, QDomNode  element2 )
{
    if (element1.nodeType() != element2.nodeType()) {
        return element1.nodeType() < element1.nodeType();
    }
    QString tag1 = element1.nodeName() ;
    QString tag2 = element2.nodeName() ;

    //qDebug() << tag1 <<tag2;
    if ( tag1 != tag2 )
        return tag1 < tag2;

    QList<QDomNode> elts1 = getChildElements(element1);
    QList<QDomNode> elts2 = getChildElements(element2);


    QString value1, value2 ;

    if(elts1.size() != elts2.size())
        return elts2.size() < elts1.size() ;

    if(elts1.size() == 0)
    {
        value1 = element1.nodeValue();
        value2 = element2.nodeValue();

        //qDebug() <<value1 << value2 << (value1 < value2);
        return value1 < value2;

    }

    qSort(elts1.begin(), elts1.end(), lessThen);
    qSort(elts2.begin(), elts2.end(), lessThen);
    //qDebug() << "comparing sorted lists";
    for(int  k = 0; k < elts1.size(); ++k) {
        if (!lessThen(elts1[k], elts2[k])) {
            if (lessThen(elts2[k], elts1[k])) {
                //qDebug() << "false!";
                return false;
            }
        }else {
            //qDebug() << "true!";
            return true;
        }
    }
    return false;
}

bool compare( QDomElement  element1, QDomElement  element2 )
{
    return ! lessThen(element1, element2) && !lessThen(element2, element1);
}

请注意,比较算法具有极高的复杂性。我花了大约 10 分钟来处理 2MB xml 文件(使用 xml-should-be-equal-to-itself 测试)

可能的测试

格式:

description (desired result)
<xml1>
<xml2>
result

测试:

different order (true) 
"<r>
 <a>x</a>
 <a>y</a>
</r>
" 
"<r>
 <a>y</a>
 <a>x</a>
</r>
" 
true 
different text (false) 
"<a>x</a>
" 
"<a>y</a>
" 
false 
different text with structure (false) 
"<a>x<b/>ddcd</a>
" 
"<a>y<b/>dede</a>
" 
false 
same structure different names (false) 
"<r>
 <a/>
 <a/>
 <b/>
</r>
" 
"<r>
 <a/>
 <b/>
 <b/>
</r>
" 
false 
same text with structure (true) 
"<a>y<b/>x</a>
" 
"<a>x<b/>y</a>
" 
true 
attributes vs text (false) 
"<a b="c"/>
" 
"<a>
 <b>c</b>
</a>
" 
false 

【讨论】:

  • 您能否进一步解释为什么您需要 lessThen 帮助器,或者指向相应的文档?为什么每次比较值时都需要区分更少和更多?您为什么不简单地查看节点是否不同(按大小、nb 个子项,...)?谢谢
  • @madptr,我不得不使用lessThen 使节点可排序:qSort(elts1.begin(), elts1.end, lessThen)。这里lessThen 是我的lessThen。排序是比较信件的最简单方法。如果我们有 2 个数组 ab 并且它们是排序的,我们只需要检查,如果 a[k] == b[k] 比较它们,但是如果它们没有排序,我们应该实现一些棘手的算法,因为既不比较 @987654365 @ 与 b[k],也不会将每对 (i, j)a[i]b[j] 进行比较,不会产生正确的结果。
  • 真的很有意义,您的解决方案很棒。您认为哪种方式可以进行时间优化?我有一个想法 Qt QDomElement 是一个瓶颈,并且本身很慢。
  • @madptr,我查看了您关于比较 xml 的其他问题:如果您现在将数据 结构 存储在一个文件中,您应该解析 xml(使用 QXmlStreamReader)根据这些知识转换成某种方便的形式,然后进行比较。
【解决方案2】:

它与 Qt 方法 NodeValue() 一起使用,该方法将 QString 转换为 QDomNode 的值,无论其类型如何。我的比较函数如下:

bool XMLtools::compare( QDomElement & element1, QDomElement & element2 )
{
    QString tag1 = element1.tagName() ;  //attribute("Name") ;
    QString tag2 = element2.tagName() ;

    if ( tag1 != tag2 )
        return false ;

    QList<QDomElement> elts1 = getChildElements(element1);
    QList<QDomElement> elts2 = getChildElements(element2);
    QDomElement c1, c2 ;

    bool these_nodes_are_equal = true ;
    bool one_comparing_tag = false ;

    QString name1, name2, value1, value2 ;

    if(elts1.size() != elts2.size())
        return false ;

    if(elts1.size() == 0)
    {
        value1 = c1.nodeValue() ;
        value2 = c2.nodeValue() ;

        if( value1 != value2 )
            return false ;
    }

    for ( int i = elts1.size() - 1 ; i > -1 ; i-- ) 
    {
        c1 = elts1.at(i);
        QString name1 = c1.tagName();

        for( int j = elts2.size() - 1 ; j > -1 ; j-- )
        {
            c2 = elts2.at(j) ;
            name2 = c2.tagName() ;

            if( name2 == name1 )
            {
                one_comparing_tag = true ;
                if ( ! compare(c1, c2) ) // c1 and c2 are potential identical nodes
                    these_nodes_are_equal = false ;
                else
                {
                    these_nodes_are_equal = true ;
                    break ;
                }
            }
        }

        if( !one_comparing_tag ) // if no node in elts2 is corresponding to node in elts1
            return false ; 

        if ( !these_nodes_are_equal ) // if no node in elts2 could compare to this node in elts1
            return false;   
    }

    return true ;
}

【讨论】:

  • 还是不行:xml1: &lt;r&gt;&lt;a&gt;&lt;/a&gt;&lt;a&gt;&lt;/a&gt;&lt;b&gt;&lt;/b&gt;&lt;/r&gt;, xml2: &lt;r&gt;&lt;a&gt;&lt;/a&gt;&lt;b&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/r&gt;。显然不同,但结果是true
  • 您声明 c1 和 c2 没有初始化,然后使用它。我猜你的意思不是value1 = c1.nodeValue() ; value2 = c2.nodeValue() ;,而是value1 = element1.nodeValue(); value2 = element2.nodeValue();
【解决方案3】:

请注意,这不考虑属性。 以下基于 octoback 的代码,但在应用于 Lol4t0 的排序版本时可能会更好(更快)。我没有彻底测试这个。

bool compareElements (QDomElement & element1, QDomElement & element2)
{
    QString tag1 = element1.tagName ();  //attribute("Name") ;
    QString tag2 = element2.tagName ();

    if (tag1 != tag2)
        return false;

    QList<QDomElement> elts1 = getChildElements (element1);
    QList<QDomElement> elts2 = getChildElements (element2);

    bool these_nodes_are_equal = true;
    bool one_comparing_tag = false;

    QString name1, name2, value1, value2;

    if (elts1.size () != elts2.size ())
        return false;

    if (elts1.size () == 0)
    {
        value1 = element1.nodeValue ();
        if (!value1.isEmpty ())
        {
            value2 = element2.nodeValue ();
            if (!value2.isEmpty ())
            {
                if (value1 != value2)
                    return false;
            }
        }

        // attributes
        QDomNamedNodeMap attributes1 = element1.attributes ();
        QDomNamedNodeMap attributes2 = element2.attributes ();
        if (attributes1.size () != attributes2.size ())
            return false;
        for (int i1 = 0; i1 < attributes1.size (); i1++)
        {
            QDomNode item1 = attributes1.item (i1);
            QString a1 = item1.nodeName ();
            if (!a1.isEmpty ())
            {
                bool found = false;
                for (int i2 = 0; i2 < attributes2.size (); i2++)
                {
                    QDomNode item2 = attributes2.item (i2);
                    QString a2 = item2.nodeName ();
                    if (a1 == a2)
                    {
                        if (item1.nodeValue () != item2.nodeValue ())
                            return false;
                        found = true;
                        break;
                    }
                }
                if (!found)
                    return false;
            }
        }
    }

    QDomElement c1, c2;
    for (int i = elts1.size () - 1; i > -1; i--)
    {
        c1 = elts1.at (i);
        name1 = c1.tagName ();

        for (int j = elts2.size () - 1; j > -1; j--)
        {
            c2 = elts2.at (j);
            name2 = c2.tagName ();

            if (name2 == name1)
            {
                one_comparing_tag = true;
                if (!compareElements (c1, c2)) // c1 and c2 are potential identical nodes
                    these_nodes_are_equal = false;
                else
                {
                    these_nodes_are_equal = true;
                    break;
                }
            }
        }

        if (!one_comparing_tag) // if no node in elts2 is corresponding to node in elts1
            return false;

        if (!these_nodes_are_equal) // if no node in elts2 could compare to this node in elts1
            return false;
    }

    return true;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多