views:

42

answers:

3

Hi, I've got two files,file a around 5mb, and file b around 66 mb. I need to find out if there's any occurnaces of the lines in file a, inside file b, and if so write them to file c.

This is the way I'm currently handling it:

ini_set("memory_limit","1000M");
set_time_limit(0);
$small_list=file("a.csv");
$big_list=file_get_contents("b.csv");
$new_list="c.csv";
$fh = fopen($new_list, 'a');
foreach($small_list as $one_line)
{
 if(stristr($big_list, $one_line) != FALSE) 
    {
    fwrite($fh, $one_line);
    echo "record found: " . $one_line ."<br>";
    }   
}

The issue is its been running(successfully) for over an hour and its maybe 3,000 lines into the 160,000 in the smaller file. Any ideas?

A: 

Try sorting the files first (espacially the large one). Then you only need to check the first few characters of each line in b, and stop (go to the next line in a) when you're past that prefix. Then you can even make an index of where in the file each characters is the first (a starts on line 0, b starts on line 1337, c on line 13986 and so on).

Emil Vikström
A: 

Try using ob_flush() and flush() in loop.

foreach($small_list as $one_line)
{
 if(stristr($big_list, $one_line) != FALSE) 
    {
    fwrite($fh, $one_line);
    echo "record found: " . $one_line ."<br>";
    }  
       @ob_flush();
        @flush();
        @ob_end_flush(); 
}
Jet
How will that speed up the search?
Emil Vikström
A: 

Build arrays with hashes as indices:

Read in file a.csv line by line and store in a_hash[md5($line)] = array($offset, $length) Read in file b.csv line by line and store in b_hash[md5($line)] = true

By using the hashes as indices you will automagically not wind up having duplicate entries.

Then for every hash that has an index in both a_hash and b_hash read in the contents of the file (using offset and length you stored in a_hash) to pull out the actual line text. If you're paranoid about hash collisions then store offset/length for b_hash as well and verify with stristr.

This will run a lot faster and use up far, far, FAR less memory.

If you want to reduce memory requirement further and don't mind checking duplicates then:

Read in file a.csv line by line and store in a_hash[md5($line)] = false
Read in file b.csv line by line, hash the line and check if exists in a_hash.
If a_hash[md5($line)] == false write to c.csv and set a_hash[md5($line)] = true

Some example code for the second suggestion:

$a_file = fopen('a.csv','r');
$b_file = fopen('b.csv','r');
$c_file = fopen('c.csv','w+');

if(!$a_file || !$b_file || !$c_file) {
    echo "Broken!<br>";
    exit;
}

$a_hash = array();

while(!feof($a_file)) {
    $a_hash[md5(fgets($a_file))] = false;
}
fclose($a_file);

while(!feof($b_file)) {
    $line = fgets($b_file);
    $hash = md5($line);
    if(isset($a_hash[$hash]) && !$a_hash[$hash]) {
        echo 'record found: ' . $line . '<br>';
        fwrite($c_file, $line);
        $a_hash[$hash] = true;
    }
}

fclose($b_file);
fclose($c_file);
Mike
This went a bit above my head, do you know a good resource where I could learn how to do this properly?
Mike
Added an example for you. Seems to work fine, but I've haven't exactly done extensive debugging. Should be enough to let you see what's happening and will run in teeny amounts of space compared to your original.
Mike
Wow, that managed to do the whole 65mb file in about 45 seconds... Thanks so much, you just saved me a really really late night. Also automagically is my new favorite word.
Mike