At the moment I have a script that will remove the row in my csv when it has already seen the sku before.
Here is the script:
<?php
// array to hold all unique lines
$lines = array();
// array to hold all unique SKU codes
$skus = array();
// index of the `sku` column
$skuIndex = -1;
// open the "save-file"
if (($saveHandle = fopen("unique.csv", "w")) !== false) {
// open the csv file
if (($readHandle = fopen("original.csv", "r")) !== false) {
// read each line into an array
while (($data = fgetcsv($readHandle, 8192, ",")) !== false) {
if ($skuIndex == -1) {
// we need to determine what column the "sku" is; this will identify
// the "unique" rows
foreach ($data as $index => $column) {
if ($column == 'sku') {
$skuIndex = $index;
break;
}
}
if ($skuIndex == -1) {
echo "Couldn't determine the SKU-column.";
die();
}
// write this line to the file
fputcsv($saveHandle, $data);
}
// if the sku has been seen, skip it
if (isset($skus[$data[$skuIndex]])) continue;
$skus[$data[$skuIndex]] = true;
// write this line to the file
fputcsv($saveHandle, $data);
}
fclose($readHandle);
}
fclose($saveHandle);
}
?>
This works fine but I am starting to need the content that is deleted.
What i need now, is to modify the code to add the same prefix to all duplicate sku's as there will only be 2 of the same sku.
I do not know where to start.
Adding a prefix to duplicates
This will add a prefix to any duplicate SKU and will then store it into the unique CSV output, e.g. XYZ123 becomes duplicate-XYZ123.
Change:
if (isset($skus[$data[$skuIndex]])) continue;
to:
if (isset($skus[$data[$skuIndex]])) $data[$skuIndex] = 'duplicate-' . $data[$skuIndex];
Fixing the duplicate header row
Add continue; after fputcsv($saveHandle, $data); Inside if ($skuIndex == -1) {. Because fputcsv(...) appears twice in the loop, it will be run twice on the first iteration of the loop.
Related
I have one excel orginal.csv file
ID Name Price
1 Xblue 12
2 Yblue 32
3 Zblue 52
And another copy.csv file
ID Name Price
1 Xblue 89
2 Yblue 43
3 Zblue 45
I want to replace rows from orginal.csv to copy.csv where ID is the same.
Can I do this manually or maybe somehow using PHP?
I search for some options on the internet, but I only found getcsv and readcsv functions that can't help me in this case. Cause this is something like updating CSV file.
It may end up in request timeout in PHP because it requires so many loops to do it. If someone can reduce the time complexity of this program then it will work. if it even works it will take a lot of time to do it.
while(! feof($f_pointer)){ //open old csv to update loop1
$ar=fgetcsv($f_pointer); // getting first row
for($i=0;$i<count($ar);$i++){ //loop2 first row array
$f_pointer2=fopen("new.csv","r"); open new csv to get data
while(! feof($f_pointer2)){ // loop3 to find ID in new csv
$ar2=fgetcsv($f_pointer2); //getting each row in array
for($j=0;$j<count($ar2);$j++){ //loop4 to compare id of old csv to new csv and update data
if($ar[i] == $ar2[j]){
foreach ($ar2 as $fields) { //loop5
fputcsv($f_pointer, $fields);
}
}
}
}
}
}
?>
I've created a little soulution. If order is important you don't have to index the array and loop through the copied array.
<?php
if(file_exists('output.csv'))
{
unlink('output.csv');
}
function fputcsv_eol($handle, $array, $delimiter = ',', $enclosure = '"', $eol = "\n") {
$return = fputcsv($handle, $array, $delimiter, $enclosure);
if($return !== FALSE && "\n" != $eol && 0 === fseek($handle, -1, SEEK_CUR)) {
fwrite($handle, $eol);
}
return $return;
}
function scanFile($sFilename, $iIndexColumn)
{
$rFile = fopen($sFilename, 'r');
$aData = array();
while(($aLine = fgetcsv($rFile)) !== false)
{
$aData[$aLine[$iIndexColumn]] = $aLine;
}
fclose($rFile);
return $aData;
}
$iIndexColumn = 0;
$iValueColum = 2;
$aOriginalData = scanFile('original.csv', 0);
$aCopyData = scanFile('copy.csv', 0);
foreach($aOriginalData as $iID => $aOriginalDatum)
{
if(array_key_exists($iID, $aCopyData))
{
$aCopyData[$iID] = $aOriginalDatum;
}
}
$rFile = fopen('output.csv', 'w');
foreach($aCopyData as $aCopyDatum)
{
fputcsv_eol($rFile, $aCopyDatum, ',', '"',"\r\n");
}
fclose($rFile);
So this is my very simple and basic account system (Just a school project), I would like the users to be able to change their password. But I am unsure on how to just replace the Password value within a row keeping all the other values the same.
CSV File:
ID,Username,Email,DateJoined,Password,UserScore,profilePics
1,Test,Test#Test.com,03/12/2014,Test,10,uploads/profilePics/Test.jpg
2,Alfie,Alfie#test.com,05/12/2014,1234,136,uploads/profilePics/Alfie.png
PHP:
("cNewPassword" = confirm new password)
<?php
session_start();
if(empty($_POST['oldPassword']) || empty($_POST['newPassword']) || empty($_POST['cNewPassword'])) {
die("ERROR|Please fill out all of the fields.");
} else {
if($_POST['newPassword'] == $_POST['cNewPassword']) {
if ($_POST['oldPassword'] == $_SESSION['password']) {
$file = "Database/Users.csv";
$fh = fopen($file, "w");
while(! feof($file)) {
$rows = fgetcsv($file);
if ($rows[4] == $_POST['oldPassword'] && $rows[1] == $_SESSION['username']) {
//Replace line here
echo("SUCCESS|Password changed!");
}
}
fclose($file);
}
die("ERROR|Your current password is not correct!");
}
die("ERROR|New passwords do not match!");
}
?>
You'll have to open file in read mode, open a temporary one in write mode, write there modified data, and then delete/rename files. I'd suggest trying to set up a real DB and work using it but if you're going for the csv, the code should look like more or less like this:
$input = fopen('Database/Users.csv', 'r'); //open for reading
$output = fopen('Database/temporary.csv', 'w'); //open for writing
while( false !== ( $data = fgetcsv($input) ) ){ //read each line as an array
//modify data here
if ($data[4] == $_POST['oldPassword'] && $data[1] == $_SESSION['username']) {
//Replace line here
$data[4] = $_POST['newPassword'];
echo("SUCCESS|Password changed!");
}
//write modified data to new file
fputcsv( $output, $data);
}
//close both files
fclose( $input );
fclose( $output );
//clean up
unlink('Database/Users.csv');// Delete obsolete BD
rename('Database/temporary.csv', 'Database/Users.csv'); //Rename temporary to new
Hope it helps.
My suggestion is a little function of mine which will turn your database data into an array which you can modify and then return to original state:
With this set of function, you simply have to precise how each row/row data are separated.
function dbToArray($db, $row_separator = "\n", $data_separator = ",") {
// Let's seperator each row of data.
$separate = explode($row_separator, $db);
// First line is always the table column name:
$table_columns =
$table_rows = array();
foreach ($separate as $key => $row) {
// Now let's get each column data out.
$data = explode($data_separator, $row);
// I always assume the first $row of data contains the column names.
if ($key == 0)
$table_columns = $data;
else if ($key > 0 && count($table_columns) == count($data)) // Let's just make sure column amount matches.
$table_rows[] = array_combine($table_columns, $data);
}
// Return an array with columns, and rows; each row data is bound with it's equivalent column name.
return array(
'columns' => $table_columns,
'rows' => $table_rows,
);
}
function arrayToDb(array $db, $row_separator = "\n", $data_separator = ",") {
// A valid db array must contain a columns and rows keys.
if (isset($db['columns']) && isset($db['rows'])) {
// Let's now make sure it contains an array. (This might too exagerated of me to check that)
$db['columns'] = (array) $db['columns'];
$db['rows'] = (array) $db['rows'];
// Now let's rewrite the db string imploding columns:
$returnDB = implode($data_separator, $db['columns']).$row_separator;
foreach ($db['rows'] as $row) {
// And imploding each row data.
$returnDB .= implode($data_separator, $row).$row_separator;
}
// Retunr the data.
return $returnDB;
}
// Faaaaaaaaaaaail !
return FALSE;
}
Let's just point out I tried these with your db example, and it works even when tested on it's own results such as : dbToArray(arrayToDb(dbToArray())) multiple times.
Hope that help. If I can be clearer don't hesitate. :)
Cheers,
You need a 3 step process to do this (create 3 loops, could be optimized to 1 or 2 loops):
Load the relevant data to memory
Update the desired data
Save the data to the file
Good luck! :)
PS. Also your passwords should never been stored in clear text, wether in memory(session) or on disk(csv), use a hasing function!
I'm looking at importing a CSV file, but this file is rather large.
What I'm looking to do, is 2 things:
1) Scan the CSV to validate values in particular fields
2) Once the file is valid, import
The CSV data can ONLY be inserted if the file is valid (All or nothing)
The problem is, I'm looping through twice, 1st to check the CSV fields are valid and then another for loop to save.
The problem is memory. I run out of memory (file is 100,000 rows with 45 fields)
Is there an easier way to do this and reduce memory? I'm using an AR implementation, would using PDO be quicker?
Thanks
EDIT:
$data = array();
// open the file and loop through
if( ($handle = fopen('details.csv', "r")) !== FALSE) {
$rowCounter = 0;
while (($rowData = fgetcsv($handle, 0, ",")) !== FALSE) {
if( 0 === $rowCounter) {
$headerRecord = $rowData;
} else {
foreach( $rowData as $key => $value) {
$data[ $rowCounter - 1][$headerRecord[ $key] ] = $value;
}
}
$rowCounter++;
}
fclose($handle);
}
$errors = array();
// loop to check through the fields for validation
for($i=0;$i<count($data);$i++) {
$row++;
if(!valid_email($data[$i]['EMAIL']))) {
$errors[] = 'Invalid Email Address';
break;
}
}
if(empty($errors)) {
for($j=0;$j<count($assocData);$j++) {
$row++;
$details = new Details();
// set the fields here
$details->email = $data[$j]['EMAIL'];
$details->save();
unset($details);
}
}
You're already looping through the data in your first foreach. Why don't you validate the fields inside that loop, if validation passes add to an array to save and only save (in a single transaction) if the loop completes.
Hello, how to read txt file using php and replacement with array?
i want read file like this:
||Search||,||s||---||Images||,||i||
this my php code:
$f = fopen("test.txt", "r");
$image= "Imgaes";
// Read line by line until end of file
while (!feof($f)) {
// Make an array using comma as delimiter
// $arrM = explode("---",fgets($f));
$arr = explode("||---||",fgets($f));
// Write links (get the data in the array)
$num = 1;
while($num <= 30) {
list($eng,$fa) = explode("||,||", $arr[$num]);
foreach($html->find('html') as $full) {
$all = $full->innertext;
}
$fe = str_replace($eng,$fa,$all);
$num = $num+1;
}
echo $fe;
}
fclose($f);
but this not work:(!!!
before the explode, parse your data in this function:
// Function arrayData
function arrayData($data){
# code...
// Create an array out of each line
$data_array=explode("\n", $data);
// Find the last key in the array
$last_key=count($data_array)-1;
// If the last line is empty revise the last key downwards until there's actually something there
while(empty($data_array[$last_key]))
{
$last_key-=1;
}
// Figure out the first key based upon the value set for the number of lines to display
$first_key=$last_key-($this->num_lines-1);
// Start a new array to store the last X lines in
$final_array=array();
// Work through the array and only add the last X lines to it.
foreach($data_array as $key => $value)
{
if($key >= $first_key && $key <= $last_key)
{
$final_array[]=$value;
}
}
return $final_array;
} // end function
$file = fopen("test.txt","r");
while($line = fgets($file)) {
$line = trim($line);
list($model,$price) = preg_split('/\s+/',$line);
if(empty($price)) {
$price = 0;
}
$sql = "UPDATE products
SET products_price=$price
WHERE products_model='$model'";
// run the sql query.
}
fclose($file);
the txt file like this:
model price
LB2117 19.49
LB2381 25.99
1, what's the meaning of list($model,$price) = preg_split('/\s+/',$line);
i know preg_split like explode, but i don't know what't the parameter meaning of the above line
2, how to skip the first record.
it's taking the results of the preg_split and assigning them to the vars $model and $price. You're looking at a parsing algorithm. Sorry if this is not enough. I have a hard time understanding the question as it is written.
Also, if I read this correctly, there is no need to skip line 1 unless you have an item with the model defined as "model" in the database.
But if you wanted to for some reason, you could add a counter...
$i = 0;
while($line = fgets($file)) {
if($i > 0)
{
$line = trim($line);
list($model,$price) = preg_split('/\s+/',$line);
if(empty($price)) {
$price = 0;
}
$sql = "UPDATE products
SET products_price=$price
WHERE products_model='$model'";
// run the sql query.
}
$i++;
}
That is a language construct that allows you to assign to multiple variables at once. You can think of it as array unpacking (preg_split returns an array). So, when you do:
<?php
list($a, $b) = explode(".","a.b");
echo $a . "\n";
echo $b . "\n";
You will get:
a
b
Having less elements in list than the array is ok, excess elements in array are ignored, but having insufficent elements in array will give you an undefined index error. For example:
list($a) = explode(".","a.b"); // ok
list($a,$b,$c) = explode(".","a.b") // error
I don't know if you meant that by skip the first record but...
$file = fopen("test.txt","r"); // open file for reading
$first = true;
while($line = fgets($file)) { // get the content file
if ($first === true) { $first = false;}//skip the first record
else{
$line = trim($line); // remove the whitespace before and after the test
// not in the middle
list($model,$price) = preg_split('/\s+/',$line); // create two variable model and price, the values are from the preg_split \s means whitespace, tab and linebreak
if(empty($price)) { // if $price is empty price =0
$price = 0;
}
$sql = "UPDATE products // sql update
SET products_price=$price
WHERE products_model='$model'";
// run the sql query.
}
}
fclose($file); //close the file