2015-06-28 17 views
4

假設我有一個很好的想法,使html鏈接標記解析器爲了探索互聯網,我使用正則表達式來解析和捕獲頁面中每一個鏈接的出現。此代碼目前正常工作,但我正在尋求添加一些成員以反映「操作狀態」。Java - 如何衡量一個匹配器處理

public class LinkScanner { 

    private static final Pattern hrefPattern = Pattern.compile("<a\\b[^>]*href=\"(.*?)\".*?>(.*?)</a>"); 

    public Collection<String> scan(String html) { 
     ArrayList<String> links = new ArrayList<>(); 
     Matcher hrefMatcher = hrefPattern.matcher(html); 
     while (hrefMatcher.find()) { 
      String link = hrefMatcher.group(1); 
      links.add(link); 
     } 
     return links; 
    } 
} 

我該如何測量這個過程?


例如:認爲這是一個假設的測量實現...

public class LinkScannerWithStatus { 

    private int matched; 
    private int total; 

    public Collection<String> scan(String html) { 
     ArrayList<String> links = new ArrayList<>(); 
     Matcher hrefMatcher = hrefPattern.matcher(html); 
     total = hrefMatcher.getFindCount(); // Assume getFindCount exists 
     while (hrefMatcher.find()) { 
      String link = hrefMatcher.group(1); 
      links.add(link); 
      matched++; // assume is a linear measurement mechanism 
     } 
     return links; 
    } 
} 

我不知道從哪裏開始。我甚至不知道,如果結合「匹配器處理」在語法上有效:S

+1

如果你想要一個非常側面思考的想法:實現一個CharSequence接口並檢查從它請求哪些字符來檢查進度。不確定它可以乾淨地完成,如果任何人調用'toString'就可能失去蹤跡。如果可以做到這將是我的首選解決方案。 –

+0

好的,實現了這一點,但我不確定它是否足夠好,稍後可能會添加其他答案,經過一番思考。 –

+0

@MaartenBodewes很高興看到一個例子..當然,如果你有時間......我看不出我在這種情況下用'CharSequence'做什麼......雖然你給了一個想法,知道在Mather正在處理的html字符的哪一部分。有一個方法'hrefMatcher.end()''返回前一匹配的結束索引。這個知道HTML的整個大小(可以通過一個簡單的'html.length();'調用知道..我認爲這可能是一個不準確但便宜的解決方案 – Victor

回答

2

不幸的是Matcher沒有監聽器接口來測量進度。有一個可能會非常昂貴。

如果您有整頁作爲String實例,那麼您可以使用region來選擇頁面的區域。你可以用它來依次掃描這些區域。然後,您可以向用戶報告您當前正在掃描的部分。您可能必須稍微回溯一下以允許區域重疊。

如果您通過使用​​來回溯以檢查比賽是否正在進行,您可以優化。如果不是,那麼你不需要回溯。

一個問題是URL的大小並沒有真正的限制,所以你需要做出選擇你支持的URL的大小。

如果你創建了一個好的正則表達式,那麼除非你正在處理真正的巨大文件,否則你不必真的報告進度。即使在這種情況下,I/O應該比HTML錨點的掃描花費更多。

+0

非常感謝Maarten,我喜歡你的想法,以解決這個問題以及你在這些場景中關於進度問題的最終建議......很高興與大家分享,我會盡力遵循這些原則,並告訴它是怎麼回事。 – Victor

+0

請注意,這是一個直接的答案。或HTML與正則表達式沒有[小馬默認警告](http://stackoverflow.com/a/1732454/589259)。巴勃羅的答案反映了這種情緒。 –

+0

從來沒有心中的那種在我的生活中,直到現在,謝謝你的數據...我使用正則表達式,工作得很好,不知道如果我真的獲得「所有鏈接」,但我正在獲得鏈接。只限於原來的問題。如果沒有辦法衡量Matcher的進展,我會將這個答案標記爲正確的。 – Victor

0

因此,要通過文檔來衡量您的進度,您希望查找總匹配數,然後在匹配匹配時更新進度並將其添加到存儲鏈接LinkedList。您可以使用以下數字計算匹配的總數: int countMatches = StringUtils.countMatches(String text,String target);然後,只需查找字符串「href」或者鏈接的標籤或其他組件,然後您就可以準確瞭解您有多少鏈接,然後您可以逐個解析它們。這並不理想,因爲它不接受regex作爲目標參數。

+0

以及..是一個很好的近似方法...不是我個人的選擇,但有好的情況..一個永遠不知道。謝謝。 – Victor

2

不考慮性能和內存問題,您可以使用DOM parser來評估HTML,這樣,當您走DOM時您可以執行給定的操作。

另一種可能性是將給定的HTML解釋爲XML並使用SAX。這是有效的,但假設可能不存在的結構。

+1

Ey Pablo Fernandez !!感謝提示的親愛的朋友。希望這些日子能享受比薩餅。是一個很好的提示...將進行調查。謝謝兄弟:D – Victor

1

根據維克托的要求,我會發表另一個答案。在這種情況下,CharSequence被實現爲另一個CharSequence的包裝。由於Matcher實例請求將字符CountingCharSequence報告給偵聽器接口。

這樣做有點危險,因爲CharSequence.toString()方法返回一個真實的String實例,這個實例不能被監控。另一方面,目前的實施看起來似乎相對簡單,並且工作起來。 toString()被調用,但似乎是在找到匹配項時填充組。儘管如此,最好寫一些單元測試。

哦,因爲我必須手動打印「100%」標記,所以可能會出現舍入錯誤或偏離錯誤。快樂調試:P

public class RegExProgress { 

    // the org. LinkScanner provided by Victor 
    public static class LinkScanner { 
     private static final Pattern hrefPattern = Pattern.compile("<a\\b[^>]*href=\"(.*?)\".*?>(.*?)</a>"); 
     public Collection<String> scan(CharSequence html) { 
      ArrayList<String> links = new ArrayList<>(); 
      Matcher hrefMatcher = hrefPattern.matcher(html); 
      while (hrefMatcher.find()) { 
       String link = hrefMatcher.group(1); 
       links.add(link); 
      } 
      return links; 
     } 
    } 

    interface ProgressListener { 
     void listen(int characterOffset); 
    } 

    static class SyncedProgressListener implements ProgressListener { 
     private final int size; 
     private final double blockSize; 
     private final double percentageOfBlock; 

     private int block; 

     public SyncedProgressListener(int max, int blocks) { 
      this.size = max; 
      this.blockSize = (double) size/(double) blocks - 0.000_001d; 
      this.percentageOfBlock = (double) size/blockSize; 

      this.block = 0; 
      print(); 
     } 

     public synchronized void listen(int characterOffset) { 
      if (characterOffset >= blockSize * (block + 1)) { 
       this.block = (int) ((double) characterOffset/blockSize); 
       print(); 
      } 
     } 

     private void print() { 
      System.out.printf("%d%%%n", (int) (block * percentageOfBlock)); 
     } 
    } 

    static class CountingCharSequence implements CharSequence { 

     private final CharSequence wrapped; 
     private final int start; 
     private final int end; 

     private ProgressListener progressListener; 

     public CountingCharSequence(CharSequence wrapped, ProgressListener progressListener) { 
      this.wrapped = wrapped; 
      this.progressListener = progressListener; 
      this.start = 0; 
      this.end = wrapped.length(); 
     } 

     public CountingCharSequence(CharSequence wrapped, int start, int end, ProgressListener pl) { 
      this.wrapped = wrapped; 
      this.progressListener = pl; 
      this.start = start; 
      this.end = end; 
     } 

     @Override 
     public CharSequence subSequence(int start, int end) { 
      // this may not be needed, as charAt() has to be called eventually 
      System.out.printf("subSequence(%d, %d)%n", start, end); 
      int newStart = this.start + start; 
      int newEnd = this.start + end - start; 
      progressListener.listen(newStart); 
      return new CountingCharSequence(wrapped, newStart, newEnd, progressListener); 
     } 

     @Override 
     public int length() { 
      System.out.printf("length(): %d%n", end - start); 
      return end - start; 
     } 

     @Override 
     public char charAt(int index) { 
      //System.out.printf("charAt(%d)%n", index); 
      int realIndex = start + index; 
      progressListener.listen(realIndex); 
      return this.wrapped.charAt(realIndex); 
     } 

     @Override 
     public String toString() { 
      System.out.printf(" >>> toString() <<< %n", start, end); 
      return wrapped.toString(); 
     } 
    } 

    public static void main(String[] args) throws Exception { 
     LinkScanner scanner = new LinkScanner(); 
     String content = new String(Files.readAllBytes(Paths.get("regex - Java - How to measure a Matcher processing - Stack Overflow.htm"))); 
     SyncedProgressListener pl = new SyncedProgressListener(content.length(), 10); 
     CountingCharSequence ccs = new CountingCharSequence(content, pl); 
     Collection<String> urls = scanner.scan(ccs); 
     // OK, I admit, this is because of an off-by one error 
     System.out.printf("100%% - %d%n", urls.size()); 

    } 
} 
+0

jajaj謝謝馬丁。我喜歡你的想法,通過裝飾的CountingCharSequence CharSequence來追蹤遊行人物的消費是非常聰明的。不知道在測量方面效果如何,但我會明確地研究它並嘗試它! – Victor