Increment number in text file - php

I want to record downloads in a text file
Someone comes to my site and downloads something, it will add a new row to the text file if it hasn't already or increment the current one.
I have tried
$filename = 'a.txt';
$lines = file($filename);
$linea = array();
foreach ($lines as $line)
{
$linea[] = explode("|",$line);
}
$linea[0][1] ++;
$a = $linea[0][0] . "|" . $linea[0][1];
file_put_contents($filename, $a);
but it always increments it by more than 1
The text file format is
name|download_count

You're doing your incrementing outside of the for loop, and only accessing the [0]th element so nothing is changing anywhere else.
This should probably look something like:
$filename = 'a.txt';
$lines = file($filename);
// $k = key, $v = value
foreach ($lines as $k=>$v) {
$exploded = explode("|", $v);
// Does this match the site name you're trying to increment?
if ($exploded[0] == "some_name_up_to_you") {
$exploded[1]++;
// To make changes to the source array,
// it must be referenced using the key.
// (If you just change $v, the source won't be updated.)
$lines[$k] = implode("|", $exploded);
}
}
// Write.
file_put_contents($filename, $lines);
You should probably be using a database for this, though. Check out PDO and MYSQL and you'll be on your way to awesomeness.
EDIT
To do what you mentioned in your comments, you can set a boolean flag, and trigger it as you walk through the array. This may warrant a break, too, if you're only looking for one thing:
...
$found = false;
foreach ($lines as $k=>$v) {
$exploded = explode("|", $v);
if ($exploded[0] == "some_name_up_to_you") {
$found = true;
$exploded[1]++;
$lines[$k] = implode("|", $exploded);
break; // ???
}
}
if (!$found) {
$lines[] = "THE_NEW_SITE|1";
}
...

one hand you are using a foreach loop, another hand you are write only the first line into your file after storing it in $a... it's making me confuse what do you have in your .txt file...
Try this below code... hope it will solve your problem...
$filename = 'a.txt';
// get file contents and split it...
$data = explode('|',file_get_contents($filename));
// increment the counting number...
$data[1]++;
// join the contents...
$data = implode('|',$data);
file_put_contents($filename, $data);

Instead of creating your own structure inside a text file, why not just use PHP arrays to keep track? You should also apply proper locking to prevent race conditions:
function recordDownload($download, $counter = 'default')
{
// open lock file and acquire exclusive lock
if (false === ($f = fopen("$counter.lock", "c"))) {
return;
}
flock($f, LOCK_EX);
// read counter data
if (file_exists("$counter.stats")) {
$stats = include "$counter.stats";
} else {
$stats = array();
}
if (isset($stats[$download])) {
$stats[$download]++;
} else {
$stats[$download] = 1;
}
// write back counter data
file_put_contents('counter.txt', '<?php return ' . var_export($stats, true) . '?>');
// release exclusive lock
fclose($f);
}
recordDownload('product1'); // will save in default.stats
recordDownload('product2', 'special'); // will save in special.stats

personally i suggest using a json blob as the content of the text file. then you can read the file into php, decode it (json_decode), manipulate the data, then resave it.

Related

Remove a line from file if it exists

I'm getting used to PHP and trying to remove a line from a file (if it exists) and resave the file.
So if I had the file
user1
user2
user3
user4
I could use
if(existsAndRemove("user3")){
do thing
}
I've tried using code similar to the code below but it sometimes bugs out and will only remove a line if it is last in the file. I have no idea how to fix this.
$data2 = file("./ats.txt");
$out2 = array();
foreach($data2 as $line2) {
if(trim($line2) != $acc) {
$out2[] = $line2;
}
}
$fp2 = fopen("./ats.txt", "w+");
flock($fp2, LOCK_EX);
foreach($out2 as $line2) {
fwrite($fp2, $line2);
}
flock($fp2, LOCK_UN);
fclose($fp2);
}
}
Any help at all would be greatly appreciated, and i would also appreciate if you could explain the code too so I could easier learn from it!!
Thank you.
If the file size is small enough that you're not worried about reading it all into memory, you could do something more functional
// Read entire file in as array of strings
$data = file("./ats.txt");
// Some text we want to remove
$acc = 'user3';
// Filter out any lines that match $acc,
// ignoring any leading or trailing whitespace
//
$filtered_data = array_filter(
$data,
function ($line) use ($acc) {
return trim($line) !== $acc;
}
)
// If something changed, write the file back out
if ($filtered_data !== $data) {
file_put_contents('./ats.txt', implode('', $filtered_data));
}
Something like this might work:
function remove_user($user) {
$file_path = "foo.txt"
$users = preg_split("[\n\r]+", file_get_contents($file_path));
foreach ($users as $i => $existing) {
if ($user == $existing) {
$users = array_splice($users, $i, 1);
file_put_contents($file_path, implode("\n", $users));
break;
}
}
}
Should be much easier since you're already using file():
$data2 = file("./ats.txt", FILE_IGNORE_NEW_LINES);
unset($data2[array_search('user3', $data2)]);
file_put_contents("./ats.txt", implode("\n", $data2));
Or to check if it exists first:
$data2 = file("./ats.txt", FILE_IGNORE_NEW_LINES);
if( ($key = array_search('user3', $data2)) !== false ) {
unset($data2[$key]);
file_put_contents("./ats.txt", implode("\n", $data2));
}

Comparing two csv files based on multiple columns and save in separate file

I have two files with same format where one has new updates and the other has older updates. There is no particular unique id column.
How can I extract the new updated lines only (with unix, PHP, AWK)?
You want to "byte" compare all lines against the other lines, so i would do:
$lines1 = file('file1.txt');
$lines2 = file('file2.txt');
$lookup = array();
foreach($lines1 as $line) {
$key = crc32($line);
if (!isset($lookup[$key])) $lookup[$key] = array();
$lookup[$key][] = $line;
}
foreach($lines2 as $line) {
$key = crc32($line);
$found = false;
if (isset($lookup[$key])) {
foreach($lookup[$key] as $lookupLine) {
if (strcmp($lookupLine, $line) == 0) {
$found = true;
break;
}
}
}
// check if not found
if (!$found) {
// output to file or do something
}
}
Note that if the files are very large this will consume quite some memory and you need to use some other mechanism, but the idea stays the same

PHP CSVImporter

I had a script called CSVimporter V3 for PHP that I used to run on a website and it worked fine. A couple of years later I've now dug out the same script to use on another project, all works okay except the CSV files are being read as one long line, as opposed to header row and multiple lines.
Here is part of the script.
Any ideas why it would be being read as a long line?
<?php
// Reference session variable for short-hand
$f = &$_SESSION['csv_file'];
// Open file - fp = file pointer
if (!$fp = #fopen($f, 'r')) {
error(L_ERROR_PREVIEW_NO_FILE);
} else {
// Array to store each row to be inserted
$batch = array();
// Row counter
$rc = 0;
// Work out starting row
switch ($_SESSION['csv_first_row']) {
case 'data':
$start_row = 0;
break;
default:
$start_row = 1;
}
// Get contents, while below preview limit and there's data to be read
while ($data = fgetcsv($fp, 1024, delimiter_to_char($_SESSION['csv_delimiter']))) {
if ($rc < $start_row) {
// Incremement counter
$rc++;
// Go to next loop
continue;
} else {
// Array to store data to be inputted
$values = array();
// Loop data
for ($i = 0; $i < count($data); $i++) {
// If data is wanted, put data into array
if (array_key_exists($i, $column_index)) {
$values[$column_index[$i]] = $data[$i];
}
}
// Sort array into correct order by index
ksort($values);
// Join values together and store in array
$batch[] = '("' . implode('", "', str_replace('"', '\"', $values)) . '","'.$accti.'","'.$impt.'")';
}
}
}
// Close the file
fclose($fp);
I added this at the top of the code and it all works now!
ini_set('auto_detect_line_endings', true);

read file matching lines using array using php

I want to be able to read through a plain text file and match a number of lines without the need to iterate over the text file multiple times. I am passing in an array with a list of strings I would like to match, which ideally, I would like to put into an array.
I can achieve the desired result using the code below, but it necessitates the reading of the text file multiple times.
function readFile($line){
$contents = file("test.txt");
if(preg_match("/$line*/i", $val)){
return($val);
}
}
Ideally, I would like to do the following:
// pass an array to the funciton which will parse the file once and match on the elements defined.
$v = readFile(array("test_1", "test_2", "test_2", "test_3"));
// return an array with the matched elements from the search.
print_r($v);
Any help would be much appreciated.
Thanks all!
$val = array();
foreach ($contents as $file) {
foreach ($line as $l) {
if (stristr($file, $l)) {
$val[] = $file;
break; // Don't need to check the other $line values
}
}
}
$val = array();
foreach ($contents as $file) {
foreach ($line as $l) {
if (stristr($file, $l) {
$val[] = $file;
}
}
}
Even if you want to stick with preg_match, the "*" is unnecessary.

PHP - How do I open files and read them then write new ones with "x" lines per file?

I posted this question here before but there were no responses. I may have done something wrong so, here it is again with some more details.
The files in the directory are named 1.txt, 2.txt, 3.txt etc.... The snippet below enters that directory, opens all the *,txt files reading them, removes the dupes and creates one file with all the unique contents. (names in this case).
$files = glob($dirname."/*.txt"); //matches all text files
$lines = array();
foreach($files as $file)
{
$lines = array_merge($lines, file($file, FILE_SKIP_EMPTY_LINES | FILE_IGNORE_NEW_LINES));
}
$lines = array_unique($lines);
file_put_contents($dirname."/allofthem.txt", implode("\n", $lines));
}
The above works great for me! Thanks to great help here at stackoverflow.
But, I desire to take it one step further.
Instead of one big duplicate free "allofthem.txt" file, how can I modify the above code to create files with a maximum of 5oo lines each from the new data?
They need to go into a new directory eg $dirname."/done/".$i.".txt" I have tried counting in the loop but my efforts are not working and ended up being a mile long.
I also attempted to push 500 into an array, increment to another array and save that way. No luck. I am just not "getting" it.
Again, this beginner needs some expert assistance. Thanks in advance.
Once you have your array of lines as per your code, you can break it into chunks of 500 lines using array_chunk, and then write each chunk to its own file:
// ... from your code
$lines = array_unique($lines);
$counter = 1;
foreach (array_chunk($lines, 500) as $chunk)
{
file_put_contents($dirname . "/done/" . $counter . ".txt", implode("\n", $chunk));
$counter++;
}
this function will get you somewhere !
function files_identical($fn1, $fn2) {
if(filetype($fn1) !== filetype($fn2))
return FALSE;
if(filesize($fn1) !== filesize($fn2))
return FALSE;
if(!$fp1 = fopen($fn1, 'rb'))
return FALSE;
if(!$fp2 = fopen($fn2, 'rb')) {
fclose($fp1);
return FALSE;
}
$same = TRUE;
while (!feof($fp1) and !feof($fp2))
if(fread($fp1, 4096) !== fread($fp2, 4096)) {
$same = FALSE;
break;
}
if(feof($fp1) !== feof($fp2))
$same = FALSE;
fclose($fp1);
fclose($fp2);
return $same;
}
Src: http://www.php.net/manual/en/function.md5-file.php#94494
$files = glob($dirname."/*.txt"); //matches all text files
$lines = array();
foreach($files as $file)
{
$lines = array_merge($lines, file($file, FILE_SKIP_EMPTY_LINES | FILE_IGNORE_NEW_LINES));
}
$lines = array_unique($lines);
$lines_per_file = 500;
$files = count($lines)/$lines_per_file;
if(count($lines) % $lines_per_file > 0) $files++;
for($i = 0; $i < $files; $i++) {
$write = array_slice($lines, $lines_per_file * $i, $lines_per_file);
file_put_contents($dirname."/done/".$i.".txt", implode("\n", $write));
}

Categories