PHP は、SplFileObject を使用してオブジェクト指向の方法で CSV ファイルを読み取る方法を既に提供しています。
$file = new SplFileObject("data.csv");
// tell object that it is reading a CSV file
$file->setFlags(SplFileObject::READ_CSV);
$file->setCsvControl(',', '"', '\\');
// iterate over the data
foreach ($file as $row) {
list ($fruit, $quantity) = $row;
// Do something with values
}
SplFileObject は CSV データをストリーミングするため、メモリ消費は非常に少なく、大きな CSV ファイルを効率的に処理できますが、ファイル I/O であるため、最速ではありません。ただし、SplFileObject は Iterator インターフェイスを実装しているため、その $file インスタンスを他の反復子にラップして反復を変更できます。たとえば、ファイルの I/O を制限するには、それを CachingIterator にラップできます。
$cachedFile = new CachingIterator($file, CachingIterator::FULL_CACHE);
キャッシュを埋めるには、$cachedFile を反復処理します。これでキャッシュがいっぱいになります
foreach ($cachedFile as $row) {
次に、キャッシュを反復するには、次のようにします
foreach ($cachedFile->getCache() as $row) {
トレードオフは明らかにメモリの増加です。
ここで、クエリを実行するために、その CachingIterator または SplFileObject を FilterIterator にラップして、csv データを反復処理するときに出力を制限することができます。
class BannedEntriesFilter extends FilterIterator
{
private $bannedEntries = array();
public function setBannedEntries(array $bannedEntries)
{
$this->bannedEntries = $bannedEntries;
}
public function accept()
{
foreach ($this->current() as $key => $val) {
return !$this->isBannedEntryInColumn($val, $key);
}
}
public function $isBannedEntryInColumn($entry, $column)
{
return isset($this->bannedEntries[$column])
&& in_array($this->bannedEntries[$column], $entry);
}
}
FilterIterator は、FilterIterator の accept メソッドのテストを満たさない内側の Iterator からすべてのエントリを省略します。上記では、禁止されたエントリの配列に対して csv ファイルの現在の行をチェックし、一致する場合、データは反復に含まれません。次のように使用します。
$filteredCachedFile = new BannedEntriesFilter(
new ArrayIterator($cachedFile->getCache())
)
キャッシュされた結果は常に配列であるため、配列を FilterIterator にラップする前に、その配列を ArrayIterator にラップする必要があります。キャッシュを使用するには、CachingIterator を少なくとも 1 回繰り返す必要があることに注意してください。上記ですでにそれを行っていると仮定します。次のステップは、禁止されたエントリを構成することです
$filteredCachedFile->setBannedEntries(
array(
// banned entries for column 0
array('foo', 'bar'),
// banned entries for column 1
array( …
)
);
それはかなり簡単だと思います。禁止されたエントリを保持する CSV データの各列に 1 つのエントリを持つ多次元配列があります。次に、インスタンスを反復処理するだけで、禁止されたエントリを持たない行のみが得られます
foreach ($filteredCachedFile as $row) {
// do something with filtered rows
}
または、結果を配列に取得するだけの場合:
$results = iterator_to_array($filteredCachedFile);
結果をさらに制限するために、複数の FilterIterator を積み重ねることができます。フィルタリングごとにクラスを書きたくない場合は、CallbackFilterIterator を見てください。これにより、実行時に受け入れロジックを渡すことができます。
$filteredCachedFile = new CallbackFilterIterator(
new ArrayIterator($cachedFile->getCache()),
function(array $row) {
static $bannedEntries = array(
array('foo', 'bar'),
…
);
foreach ($row as $key => $val) {
// logic from above returning boolean if match is found
}
}
);