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());
}
}
假设我有一个绝妙的想法,即制作一个 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());
}
}