Java - 如何衡量一个Matcher处理

Java - How to measure a Matcher processing

假设我有一个绝妙的想法,即制作一个 html link 标签解析器以探索互联网,并且我使用正则表达式来解析和捕获每次出现的 link 在一页中。此代码目前工作正常,但我正在寻求添加一些成员以反映 "operation status"。

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;
    }
}

我不知道从哪里开始..我什至不知道连词 "Matcher processing" 在语法上是否有效 :S

因此,要通过文档衡量您的进度,您需要找到匹配的总数,然后逐个匹配地更新进度并将它们添加到存储的 links LinkedList。

您可以使用以下方法计算匹配总数: int countMatches = StringUtils.countMatches(字符串文本,字符串目标);

那么,只需查找字符串 "href" 或 link 的标签或某些其他组件,然后您就会准确地了解有多少 link有了,那你就可以一一解析了。这并不理想,因为它不接受正则表达式作为目标参数。

遗憾的是Matcher 没有用于衡量进度的侦听器界面。拥有一个可能会非常昂贵。

如果您将整个页面作为 String 实例,那么您可以使用页面的 region 到 select 区域。您可以使用它按顺序扫描这些区域。然后您可以向用户报告您当前正在扫描的部分。您可能需要回溯一下以允许区域重叠。

如果您通过使用 hitEnd 回溯来检查比赛是否正在进行,您可以进行优化。如果不是那么你不需要回溯。

一个问题是 URL 的大小并没有真正的限制,因此您需要选择您希望支持的 URL 的大小。

如果您创建了一个良好的正则表达式,那么您实际上不必报告进度,除非您正在处理真正巨大的文件。即使在那种情况下,I/O 也应该比扫描 HTML 锚点有更多的开销。

撇开性能和内存问题不谈,您可以使用 DOM parser 来评估 HTML,这样,当您走 DOM 时,您可以执行给定的操作。

另一种可能性是将给定的 HTML 解释为 XML 并使用 SAX。这是有效的,但假设一个可能不存在的结构。

应 Victor 的要求,我会 post 另一个答案。在这种情况下 CharSequence 被实现为另一个 CharSequence 的包装器。当 Matcher 实例请求字符时,CountingCharSequence 向侦听器接口报告。

这样做有点危险,因为 CharSequence.toString() 方法 returns 一个无法监控的真实 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());

    }
}