簡體   English   中英

快速自然排序瀏覽器中的大型XML文件?

[英]Fast Natural Sort for Large XML File in Browser?

我現在遇到一個問題,這是我們團隊無法控制的服務器上當前限制的結果。

我們有一項工作應該由數據庫完成,但是我們被迫使用XML文件並使用Javascript / jQuery對其進行解析。 我們甚至沒有腳本的寫訪問權限(僅通過FTP帳戶)...我們不想談論它,但這就是我們所得到的。

由於這些限制,問題在於我們需要解析一個大約500kb的大型XML文件,其中包含文件名/編號/ URL的1700余條記錄。

該數字非常復雜,例如“ 31-2b-1029E”,並混入“ T2315342”之類的內容。

因此,我認為我需要使用一種稱為“自然排序”的東西(感謝stackoverflow)。

無論如何,我在這里嘗試使用此腳本:

/*
 * Reference: http://www.overset.com/2008/09/01/javascript-natural-sort-algorithm/
 * Natural Sort algorithm for Javascript - Version 0.6 - Released under MIT license
 * Author: Jim Palmer (based on chunking idea from Dave Koelle)
 * Contributors: Mike Grier (mgrier.com), Clint Priest, Kyle Adams, guillermo
 */
function naturalSort (a, b) {
    var re = /(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?$|^0x[0-9a-f]+$|[0-9]+)/gi,
        sre = /(^[ ]*|[ ]*$)/g,
        dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,
        hre = /^0x[0-9a-f]+$/i,
        ore = /^0/,
        // convert all to strings and trim()
        x = a.toString().replace(sre, '') || '',
        y = b.toString().replace(sre, '') || '',
        // chunk/tokenize
        xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
        yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
        // numeric, hex or date detection
        xD = parseInt(x.match(hre)) || (xN.length != 1 && x.match(dre) && Date.parse(x)),
        yD = parseInt(y.match(hre)) || xD && y.match(dre) && Date.parse(y) || null;
    // first try and sort Hex codes or Dates
    if (yD)
        if ( xD < yD ) return -1;
        else if ( xD > yD ) return 1;
    // natural sorting through split numeric strings and default strings
    for(var cLoc=0, numS=Math.max(xN.length, yN.length); cLoc < numS; cLoc++) {
        // find floats not starting with '0', string or 0 if not defined (Clint Priest)
        oFxNcL = !(xN[cLoc] || '').match(ore) && parseFloat(xN[cLoc]) || xN[cLoc] || 0;
        oFyNcL = !(yN[cLoc] || '').match(ore) && parseFloat(yN[cLoc]) || yN[cLoc] || 0;
        // handle numeric vs string comparison - number < string - (Kyle Adams)
        if (isNaN(oFxNcL) !== isNaN(oFyNcL)) return (isNaN(oFxNcL)) ? 1 : -1; 
        // rely on string comparison if different types - i.e. '02' < 2 != '02' < '2'
        else if (typeof oFxNcL !== typeof oFyNcL) {
            oFxNcL += ''; 
            oFyNcL += ''; 
        }
        if (oFxNcL < oFyNcL) return -1;
        if (oFxNcL > oFyNcL) return 1;
    }
    return 0;
}

並使用:

// Natural Sort (disabled because it is super freaking slow.... need xsl transform sorting instead)
var sortedSet = $(data).children("documents").children("document").sort(function(a, b) {
    return naturalSort($(a).children('index').text(), $(b).children('index').text());
});

這在我們其他較小的XML文件上也能正常工作,但對於500kb的巨型文件Safari(v4)來說,它掛了最多幾分鍾的時間就可以將其整理出來,而Firefox(最新的)則需要大約10秒鍾來處理(仍然不好,但至少是理智的)。

我還發現了另一個較小/較輕的腳本Alphanum

function alphanum(a, b) {
  function chunkify(t) {
    var tz = [], x = 0, y = -1, n = 0, i, j;

    while (i = (j = t.charAt(x++)).charCodeAt(0)) {
      var m = (i == 46 || (i >=48 && i <= 57));
      if (m !== n) {
        tz[++y] = "";
        n = m;
      }
      tz[y] += j;
    }
    return tz;
  }

  var aa = chunkify(a);
  var bb = chunkify(b);

  for (x = 0; aa[x] && bb[x]; x++) {
    if (aa[x] !== bb[x]) {
      var c = Number(aa[x]), d = Number(bb[x]);
      if (c == aa[x] && d == bb[x]) {
        return c - d;
      } else return (aa[x] > bb[x]) ? 1 : -1;
    }
  }
  return aa.length - bb.length;
}

這對於Safari而言運行速度更快,但仍會鎖定瀏覽器一分鍾左右。

我做了一些研究,似乎有人建議使用XSL對XML條目進行排序,由於將其內置到瀏覽器中而不是在JavaScript之上運行,因此顯然更快。

顯然有幾種不同的實現方式, Sarissa被多次提及,sourceforge頁面似乎表明最后一次更新發生在2011-06-22。

還有其他選擇,例如xslt.js

我的問題是:

  1. XSL是針對此特定問題的最佳排序選項嗎?
  2. 如果是這樣,如何使用XSL進行自然排序? (訪問資源?)
  3. 如果對兩個問題都同意,我應該使用哪個庫以獲得最佳兼容性和最快速度?
  4. 如果XSL不是最佳選擇,那么哪個是?

感謝您關注我的問題。

好問題,+ 1。

這是一個XSLT 1.0解決方案 (存在一個XSLT 2.0解決方案,它更簡單,更容易編寫,並且可能更高效,但是5種主流瀏覽器都沒有配備XSLT 2.0處理器):

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common" exclude-result-prefixes="xml">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:variable name="vDigits" select="'0123456789'"/>

 <xsl:variable name="vPadding" select=
 "'                    '"/>

 <xsl:variable name="vMaxNumLength"
      select="string-length($vPadding)"/>

 <xsl:template match="/">
  <xsl:variable name="vrtfPass1">
   <t>
    <xsl:apply-templates/>
   </t>
  </xsl:variable>

  <xsl:variable name="vPass1" select="ext:node-set($vrtfPass1)"/>

  <t>
    <xsl:for-each select="$vPass1/*/*">
     <xsl:sort select="@sortMe"/>

     <xsl:copy>
      <xsl:value-of select="."/>
     </xsl:copy>
    </xsl:for-each>
  </t>
 </xsl:template>

 <xsl:template match="str">
   <str>
    <xsl:apply-templates select="text()" mode="normalize"/>
    <xsl:copy-of select="text()"/>
   </str>
 </xsl:template>

 <xsl:template match="text()" mode="normalize" name="normalize">
  <xsl:param name="pText" select="."/>
  <xsl:param name="pAccum" select="''"/>

  <xsl:choose>
   <xsl:when test="not(string-length($pText) >0)">
     <xsl:attribute name="sortMe">
       <xsl:value-of select="$pAccum"/>
     </xsl:attribute>
   </xsl:when>
   <xsl:otherwise>
    <xsl:variable name="vChar1" select="substring($pText,1,1)"/>

    <xsl:choose>
     <xsl:when test="not(contains($vDigits,$vChar1))">
       <xsl:variable name="vDig1" select=
       "substring(translate($pText,
                            translate($pText, $vDigits, ''),
                            ''
                            ),
                  1,1)"/>
       <xsl:variable name="vDig">
        <xsl:choose>
         <xsl:when test="string-length($vDig1)">
          <xsl:value-of select="$vDig1"/>
         </xsl:when>
         <xsl:otherwise>0</xsl:otherwise>
        </xsl:choose>
       </xsl:variable>

       <xsl:variable name="vNewText" select=
        "substring-before(concat($pText,$vDig), $vDig)"/>

       <xsl:call-template name="normalize">
        <xsl:with-param name="pText" select=
         "substring($pText, string-length($vNewText)+1)"/>
        <xsl:with-param name="pAccum" select=
        "concat($pAccum, $vNewText)"/>
       </xsl:call-template>
     </xsl:when>

     <xsl:otherwise>
      <xsl:variable name="vNonDig1" select=
      "substring(translate($pText, $vDigits, ''),1,1)"/>

      <xsl:variable name="vNonDig">
        <xsl:choose>
         <xsl:when test="string-length($vNonDig1)">
          <xsl:value-of select="$vNonDig1"/>
         </xsl:when>
         <xsl:otherwise>Z</xsl:otherwise>
        </xsl:choose>
      </xsl:variable>

      <xsl:variable name="vNum" select=
           "substring-before(concat($pText,'Z'),$vNonDig)"/>

      <xsl:variable name="vNumLength" select=
       "string-length($vNum)"/>

      <xsl:variable name="vNewText" select=
       "concat(substring($vPadding,
                         1,
                         $vMaxNumLength -$vNumLength),
               $vNum
               )"/>

       <xsl:call-template name="normalize">
        <xsl:with-param name="pText" select=
         "substring($pText, $vNumLength +1)"/>
        <xsl:with-param name="pAccum" select=
        "concat($pAccum, $vNewText)"/>
       </xsl:call-template>
     </xsl:otherwise>
    </xsl:choose>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>
</xsl:stylesheet>

在以下XML文檔上應用此轉換時

<t>
 <str>Allegia 6R Clasteron</str>
 <str>200X Radonius</str>
 <str>Xiph Xlater 10000</str>
 <str>1000X Radonius Maximus</str>
 <str>Callisto Morphamax 6000 SE</str>
 <str>10X Radonius</str>
 <str>20X Radonius</str>
 <str>30X Radonius</str>
 <str>20X Radonius Prime</str>
 <str>40X Radonius</str>
 <str>Allegia 50 Clasteron</str>
 <str>Allegia 500 Clasteron</str>
 <str>Allegia 50B Clasteron</str>
 <str>Allegia 51 Clasteron</str>
 <str>Alpha 100</str>
 <str>Alpha 2</str>
 <str>Alpha 200</str>
 <str>Alpha 2A</str>
 <str>Alpha 2A-8000</str>
 <str>Alpha 2A-900</str>
 <str>Callisto Morphamax</str>
 <str>Callisto Morphamax 500</str>
 <str>Callisto Morphamax 5000</str>
 <str>Callisto Morphamax 600</str>
 <str>Callisto Morphamax 6000 SE2</str>
 <str>Callisto Morphamax 700</str>
 <str>Callisto Morphamax 7000</str>
 <str>Xiph Xlater 2000</str>
 <str>Xiph Xlater 300</str>
 <str>Xiph Xlater 40</str>
 <str>Xiph Xlater 5</str>
 <str>Xiph Xlater 50</str>
 <str>Xiph Xlater 500</str>
 <str>Xiph Xlater 5000</str>
 <str>Xiph Xlater 58</str>
</t>

產生想要的,正確的“自然排序”結果

<t>
   <str>10X Radonius</str>
   <str>20X Radonius</str>
   <str>20X Radonius Prime</str>
   <str>30X Radonius</str>
   <str>40X Radonius</str>
   <str>200X Radonius</str>
   <str>1000X Radonius Maximus</str>
   <str>Allegia 6R Clasteron</str>
   <str>Allegia 50 Clasteron</str>
   <str>Allegia 50B Clasteron</str>
   <str>Allegia 51 Clasteron</str>
   <str>Allegia 500 Clasteron</str>
   <str>Alpha 2</str>
   <str>Alpha 2A</str>
   <str>Alpha 2A-900</str>
   <str>Alpha 2A-8000</str>
   <str>Alpha 100</str>
   <str>Alpha 200</str>
   <str>Callisto Morphamax</str>
   <str>Callisto Morphamax 500</str>
   <str>Callisto Morphamax 600</str>
   <str>Callisto Morphamax 700</str>
   <str>Callisto Morphamax 5000</str>
   <str>Callisto Morphamax 6000 SE</str>
   <str>Callisto Morphamax 6000 SE2</str>
   <str>Callisto Morphamax 7000</str>
   <str>Xiph Xlater 5</str>
   <str>Xiph Xlater 40</str>
   <str>Xiph Xlater 50</str>
   <str>Xiph Xlater 58</str>
   <str>Xiph Xlater 300</str>
   <str>Xiph Xlater 500</str>
   <str>Xiph Xlater 2000</str>
   <str>Xiph Xlater 5000</str>
   <str>Xiph Xlater 10000</str>
</t>

重要假設 :此解決方案假定數字不超過40位。 盡管在大多數實際情況中都是如此,但如果出現這種限制不足的情況,則很容易修改此解決方案以將限制值接受為外部/全局參數。

最后,性能

處理與上面類似的XML文檔,但是具有1700個str元素需要0.659秒。 在我8歲的奔騰單核3GHz CPU和2GB RAM計算機上。

說明

  1. 這是兩遍解決方案。

  2. 在第一遍中,所有節點均按“原樣”復制,但sortMe屬性已添加到每個str元素。 此屬性包含str的唯一文本節點子級的字符串值-其中任何數字都用空格補齊,固定總長度為40。

  3. 在Pass 2中,我們使用單個sort鍵( sortMe屬性)按字母順序對所有str元素進行排序。

現在,回答所有4個原始問題

我的問題是:

XSL是針對此特定問題的最佳排序選項嗎?
如果是這樣,如何使用XSL進行自然排序? (訪問資源?)
如果對兩個問題都同意,我應該使用哪個庫以獲得最佳兼容性和最快速度?
如果XSL不是最佳選擇,那么哪個是?

答案

  1. 最佳排序算法的任何實現(與語言無關)都應足夠。 在這方面,XSLT是一個不錯的選擇。

  2. 上面的代碼提供了“自然”排序的完整且准確的XSLT實現。

  3. 無需庫-只需按原樣使用上面的代碼即可。 如果需要幫助如何從PL調用轉換,請查閱相應的文檔。

  4. 包括任何PL,XSLT以及最佳排序算法的實現都是合適的選擇。

輔助問題的幾個答案:

(a)Sarissa不是XSLT處理器,它是Javascript包裝器層,可為作為瀏覽器一部分提供的XSLT處理器提供通用的Javascript API。

(b)xslt.js是一個無效項目,試圖在Javascript中實現XSLT處理器。 算了,那是歷史。

朝此方向進行的最新工作是Saxon-CE,它目前處於alpha發行版(這是用Java編寫的,並使用GWT交叉編譯為Javascript)。 完成后,這將在瀏覽器中提供XSLT 2.0。 服務器端Saxon的排序規則可為您提供“自然排序”( <xsl:sort collation='http://saxon.sf.net/collation?alphanumeric=yes'/> ),但是在當前版本中不可用撒克遜公元-CE。

(PS以前我沒有遇到過“自然排序”這個名字。謝謝。)

排序函數的調用次數比數組中要排序的元素的次數更多,實際上要調用的次數更多。 對於您的1700個元素進行排序,根據瀏覽器的不同,比較函子可能會被調用10,000至750,000次。由於排序比較功能很慢,因此每個元素執行一次繁重的操作並存儲結果將使您受益匪淺,然后對存儲的結果進行排序。

我敢打賭,主要的問題是您在排序函數中使用了jquery。 那一定很貴。 實際的自然排序比較可能相對較快。 我不知道您的xml結構,但是如果您可以在排序函數中拋棄jquery,請嘗試將元素引用復制到新數組中,這是線性時間。 然后,您對數組進行排序。 然后,循環遍歷現在已排序的數組,並使用元素引用在xml文檔中設置順序。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM