6つのcsvスタイルのファイルと2つの不適切にレイアウトされた.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つのルックアップ方法を使用していますが、これはそのうちの1つにすぎません)、プロセスは約4つの異なるインデックス作成を完了しました。 1分以内に50,000レコードの識別子。12時間と比較すると、それは瞬時です。
いくつかのメモ(2010年6月6日更新):
- tokensBeforeのパターンマッチング動作がまだ必要です。
- 必要なすべてのID番号は、必ずしも行の固定位置から始まる必要はありませんが、IDトークンの後に対応するオブジェクトの名前が続くことが保証されています。
- 理想的には、結果の開始位置ではなく、文字列をintなどとして返したいと思います。
検索ごとに1ミリ秒節約できたとしても、私を助けるものは何でも役立つので、すべての入力に感謝します。ありがとうございました!
使用シナリオ1:ファイルAにオブジェクトのリストがあり、古いスタイルのシステムではファイルAにはないID番号があります。ただし、別のcsvスタイルのファイル(ファイルB)にある可能性があります。まだ.txtレポート(ファイルC)にあり、それぞれにここでは役に立たない他の情報がたくさん含まれているため、ファイルBでオブジェクトのフルネーム(2番目の列にあるため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の実行回数を減らすことができます。
もう1つの可能な最適化は、これです。すべてのtokensAfterを連結し、(.*)
事前に配置します。これにより、コンパイルされる正規表現(とにかくすべてリテラル)の数が約2/3削減され、すべての行から「潜在的なトークン」を保持する代わりに、そのグループからテキストを引き出すことができます。その上のID。それもやりがいがありますか?
findWithinHorizonを呼び出した後、java.util.Scannerに現在のトークンより前のトークンを返すようにできれば、上記の状況は解決できます。