我正在构建一个过程,从6个csv样式的文件和两个布局不佳的.txt报告中提取数据并构建输出CSV,我完全清楚会有一些开销搜索所有空白数千个有时间,但我从未预料到转换大约50,000条记录需要12个小时。
我的手动匹配代码的摘录(我知道我使用这样的令牌列表是可怕的,但这是我能想到的最好的事情):
public static String lookup(Pattern tokenBefore,
List<String> tokensAfter)
{
String result = null;
while(_match(tokenBefore)) { // block until all input is read
if(id.hasNext())
{
result = id.next(); // capture the next token that matches
if(_matchImmediate(tokensAfter)) // try to match tokensAfter to this result
return result;
} else
return null; // end of file; no match
}
return null; // no matches
}
private static boolean _match(List<String> tokens)
{
return _match(tokens, true);
}
private static boolean _match(Pattern token)
{
if(token != null)
{
return (id.findWithinHorizon(token, 0) != null);
} else {
return false;
}
}
private static boolean _match(List<String> tokens, boolean block)
{
if(tokens != null && !tokens.isEmpty()) {
if(id.findWithinHorizon(tokens.get(0), 0) == null)
return false;
for(int i = 1; i <= tokens.size(); i++)
{
if (i == tokens.size()) { // matches all tokens
return true;
} else if(id.hasNext() && !id.next().matches(tokens.get(i))) {
break; // break to blocking behaviour
}
}
} else {
return true; // empty list always matches
}
if(block)
return _match(tokens); // loop until we find something or nothing
else
return false; // return after just one attempted match
}
private static boolean _matchImmediate(List<String> tokens)
{
if(tokens != null) {
for(int i = 0; i <= tokens.size(); i++)
{
if (i == tokens.size()) { // matches all tokens
return true;
} else if(!id.hasNext() || !id.next().matches(tokens.get(i))) {
return false; // doesn't match, or end of file
}
}
return false; // we have some serious problems if this ever gets called
} else {
return true; // empty list always matches
}
}
基本上想知道如何在高效的字符串搜索中工作(Boyer-Moore或类似的)。我的扫描程序id
正在扫描java.util.String
,计算缓冲到内存将减少I / O,因为此处的搜索在相对较小的文件上执行了数千次。与扫描BufferedReader(FileReader(File))相比,性能提升可能不到1%,该过程看起来仍然需要很长时间。
我还跟踪了执行情况,整个转换过程的缓慢绝对是在查找方法的第一个和最后一个之间。实际上,我运行了一个快捷方式来计算.csv样式文件中各种标识符的出现次数(我使用2种查找方法,这只是其中之一)并且过程完成索引大约4种不同不到一分钟就能获得50,000条记录的标识符。与12小时相比,那是即时的。
部分说明(2010年6月6日更新):
任何可以帮助我的东西,即使它每次搜索节省1ms,也会有所帮助,所以所有输入都值得赞赏。三江源!
使用场景1:我有一个文件A中的对象列表,旧式系统中的对象编号不在文件A中。但是,它可能位于另一个csv样式的文件中(文件B) )或者可能仍然在.txt报告(文件C)中,每个报告都包含一堆在这里没用的其他信息,因此需要搜索文件B以查找对象的全名(1个令牌,因为它将驻留在任何给定行的第二列),然后第一列应该是ID号。如果这不起作用,那么我们必须将搜索令牌按空格分成单独的标记,然后再搜索文件C以获取这些标记。
通用代码:
String field;
for (/* each record in file A */)
{
/* construct the rest of this object from file A info */
// now to find the ID, if we can
List<String> objectName = new ArrayList<String>(1);
objectName.add(Pattern.quote(thisObject.fullName));
field = lookup(objectSearchToken, objectName); // search file B
if(field == null) // not found in file B
{
lookupReset(false); // initialise scanner to check file C
objectName.clear(); // not using the full name
String[] tokens = thisObject.fullName.split(id.delimiter().pattern());
for(String s : tokens)
objectName.add(Pattern.quote(s));
field = lookup(objectSearchToken, objectName); // search file C
lookupReset(true); // back to file B
} else {
/* found it, file B specific processing here */
}
if(field != null) // found it in B or C
thisObject.ID = field;
}
objectName标记都是大写单词,其中包含可能的连字符或撇号,用空格(一个人的名字)分隔。
根据aioobe的回答,我已经为我的常量搜索令牌预编译了正则表达式,在这种情况下只是\r\n
。在另一个进程中注意到的加速比大约是20倍,我编译了[0-9]{1,3}\\.[0-9]%|\r\n|0|[A-Z'-]+
,尽管在上面的代码中没有注意到\r\n
。沿着这些方向努力,我想知道:
如果只有可用的匹配项会在以非空格字符开头的行上匹配\r\n[^ ]
,我会更好吗?它可能会减少_match执行次数。
另一种可能的优化是:连接所有标记后,并预先放置(.*)
。它会减少大约2/3编译的正则表达式(所有这些都是字面意思),并且还希望允许我从该分组中提取文本而不是保留每行的“潜在令牌”一个ID就可以了。这也值得吗?
如果我可以让java.util.Scanner在调用findWithinHorizon之后返回当前的令牌,那么上述情况就可以得到解决。
答案 0 :(得分:2)
开始时的事情:每次运行id.next().matches(tokens.get(i))
时,都会执行以下代码:
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(input);
return m.matches();
编译正则表达式并不重要,您应该考虑在程序中一劳永逸地编译模式:
pattern[i] = Pattern.compile(tokens.get(i));
然后只需调用类似
的内容pattern[i].matcher(str).matches()