Replace array in string from position - php

I get trouble on replacing the word into special characters
First, I read the txt file and store each line into $line
and put the special character that I want to change into $table array.
How do I change $line with special character $table array one by one based on the position for example, the txt include three words:
pads
password
qwerty
so the program should show
p#ds
p#d$
p#ssword
p#$sword
p#$$word
p#$$w0rd
qwerty
Now my work just change all special characters into a new word.
but how to change it using foreach / for loop one by one based on the position
My code as follows
<?php
$file = fopen("text.txt", "r");
while(!feof($file)) {
$line = fgets($file);
$line = rtrim ($line);
$table = array(
'a'=>'#', 'o'=>'0', 's'=>'$',
);
$length = strlen($line);
for ($i=0 ; $i<$length ; $i++){
$line = strtr ($line, $table);
echo $line."<br>";
};
}
fclose($file);
?>

This should do the job (haven't tested it):
$char_array = str_split($line);
$replaced = FALSE;
foreach($char_array as $char) {
if(array_key_exists($char, $table)) {
$line = str_replace($char, $table[$char], $line, 1);
echo $line."<br>";
$replaced = TRUE;
}
}
if(!$replaced)
echo $line."<br>";
By setting the count argument of str_replace to 1, you make sure only the current character is replaced and not all of them.

Instead of strtr(), use preg_replace() like this:
for ($i=0 ; $i<$length ; $i++){
if (array_key_exists($line[$i], $table)) {
$line = preg_replace('/' . $line[$i] . '/', $table[$line[$i]], $line, 1);
echo $line."<br>";
}
};

Related

Replace character of a particular line with incremental number per line [duplicate]

This question already has answers here:
Replace substrings with an incremented counter value
(5 answers)
Closed 2 years ago.
I have a text file (data.txt) with the following lines
# one
# two
a-two
b-two
c-two
# three
a-three
b-three
# four
I would like to replace # with incremental number per line so to be like this
1 one
2 two
a-two
b-two
c-two
3 three
a-three
b-three
4 four
I have try to do this
<?php
$path_to_file = 'data.txt';
$what_to_replace = '#';
$i = 0; // initial count
$file_contents = file_get_contents($path_to_file);
$file_contents = str_replace($what_to_replace, $i++,$file_contents);
file_put_contents($path_to_file,$file_contents);
?>
it did changed in all lines with # but not incrementally
Here's a simple way via preg_replace_callback():
$i = 1;
$file_contents = preg_replace_callback('/^#/m', function($match) use (&$i) {
return $i++;
}, $file_contents);
The m (multiline) flag is important - it will force our pattern to match # at the start of any line, not just the first.
I think you have to iterate over all the lines and replace the hash one by one. Of course, you can use some preg_replace as well to only replace the # at the line beginning: https://www.php.net/manual/en/function.preg-replace.php
$result = '';
$handle = fopen("data.txt", "r");
$i = 1;
if ($handle) {
while (($line = fgets($handle)) !== false) {
$content .= str_replace('#', $i++,$line) . "\n";
}
fclose($handle);
} else {
// error opening the file.
}
file_put_contents('data.txt', $result);
$path_to_file = 'data.txt';
$what_to_replace = '#';
$i = 1; // initial count
$file_contents = file_get_contents($path_to_file);
$lines = explode(PHP_EOL, $file_contents);
$new_lines = [];
foreach ($lines as $line) {
if (strpos($line, '#') !== false) {
$new_lines[] = str_replace('#', $i, $line);
$i++;
} else {
$new_lines[] = $line;
}
}
file_put_contents($path_to_file, implode(PHP_EOL, $new_lines));
We can split the text line-by-line, then process each line individually, looking for a # at the start of the line & modifying the output in those cases.
The preg_replace_callback approach is better than this one, IMO.
$text = file_get_contents(...);
$lines = explode("\n", $text); // split by unix-style line break
$out = []; // For storing the modified lines
$i=0;
foreach ($lines as $line){
if (substr($line,0,1)=='#'){
//replace the first character & increment the counter if line starts with #
$i++;
$line = $i.substr($line,1);
}
//append the SOMETIMES modified line to the output
$out[] = $line;
}
// convert array of lines to string
$fixedText = implode("\n", $out);
See https://stackoverflow.com/a/28725803/802469 for a more robust version of the explode("\n", $text) line.
Personally, I like the $out = []; ...; implode(...) approach. I find it easier to work with & to visualize in my mind. Using an $out = ""; $out .= $line."\n"; approach would work just as well. I suspect any performance increase from going with string would be negligible, but might lower your carbon footprint a small amount.
If you have serious performance/resource concerns (extremely large files) then the fopen()/while fgets() approach would be better, and fopen/fwrite() could be used to append each output line to a file without using up memory for an output string.

How to dynamically replace text between symbols and replace with a constant in PHP?

I have a problem where I need to search a HTML page/snippet and replace any value that is between four percentile symbols and convert to a constant variable, e.g. %%THIS_CONSTANT%% becomes THIS_CONSTANT.
Right now I am searching through the page, line by line, and I am able to find matches and replace them by using preg_match_all and preg_replace.
$file_scan = fopen($directory.$file, "r");
if ($file_scan) {
while (($line = fgets($file_scan)) !== false) {
if(preg_match_all('/\%%(.*?)\%%/', $line, $matches)){
foreach($matches as $match){
foreach($match as $m){
$repair = preg_replace('/\%%(.*?)\%%/', $m, $m);
if(preg_match('/\%%(.*?)\%%/', $m, $m)){
} else {
echo $repair.' '.$j;
$j++;
}
}
$lines[$i] = preg_replace('/\%%(.*?)\%%/', constant($repair), $line);
}
} else {
$lines[$i] = $line;
}
$i++;
}
$template[$name] = implode("", $lines);
fclose($file_scan);
}
What this code is not able to do is find and replace multiple matches on a single line. For instance, if there is a line with:
<img src="%%LOGO_IMAGE%%"><h1>%%TITLE%%</h1>
The above code would replace both items with the same value (TITLE). It would also give the error couldn't find constant on the first loop, but work correctly on the second.
This happens very rarely, but I just wish to know how to modify multiple instances on a single line just to be safe.
Edit:
I am able to replace the majority of the code with this:
$file_scan = fopen($directory.$file, "r");
if ($file_scan) {
while (($line = fgets($file_scan)) !== false) {
$line = preg_replace('/\%%(.*?)\%%/', '$2'.'$1', $line);
echo $line;
}
fclose($file_scan);
My last issue is changing the replaced items to constants. Is that possible?
Final Edit:
With the help from Peter Bowers suggestion, I used preg_replace_callback to add the ability to change the keyword to a constant:
foreach($filenames as $file){
$name = str_replace('.html', '', $file);
$template[$name] = preg_replace_callback('/\%%(.*?)\%%/', function($matches){
$matches[0] = preg_replace('/\%%(.*?)\%%/', '$1', $matches[0]);
return constant($matches[0]);
}, file_get_contents($directory.$file));
}
return $template;
Here's a much simpler implementation.
$file_scan = fopen($directory.$file, "r");
if ($file_scan) {
$out = '';
while (($line = fgets($file_scan)) !== false) {
$out .= preg_replace('/\%%(.*?)\%%/', '$1', $line);
$i++;
}
$template[$name] = $out;
fclose($file_scan);
}
Or, even simpler:
$str = file_get_contents($directory.$file);
$template[$name] = preg_replace('/\%%(.*?)\%%/', '$1', $str);
And, since we're going totally simple here...
$template[$name] = preg_replace('/\%%(.*?)\%%/', '$1', file_get_contents($directory.$file));
(Obviously you are losing some of your error checking capabilities as we approach the one-liner, but - hey - I was having fun... :-)
Try with this:
<?php
define('TITLE', 'Title');
define('LOGO_IMAGE', 'Image');
$lines = array();
$file_scan = fopen($directory.$file, "r");
if ($file_scan) {
while (($line = fgets($file_scan)) !== false) {
if(preg_match_all('/\%%(.*?)\%%/', $line, $matches)){
for($i = 0; $i < count($matches[0]); $i++) {
$line = str_replace($matches[0][$i], constant($matches[1][$i]), $line);
}
$lines[] = $line;
print_r($line);
}
}
}
$template[$name] = implode("", $lines);
fclose($file_scan);
?>

PHP modify TXT file

I have a ID.txt file that looks like this:
"http://something.net/something-ak/41389_718783565_1214898307_q.jpg"
"http://something.net/something-ak/372142_106502518141813_1189943482_q.jpg"
and so on
I want to use PHP to open the file and remove everything before the first " _ " and everything after the second " _ " so I wind up with this:
718783565
106502518141813
and so on
Thing is I don't really know how to do that.
This is what I have so far:
<?PHP
$file_handle = fopen("ID.txt", "rb");
while (!feof($file_handle) ) {
$line_of_text = fgets($file_handle);
$parts = explode('\n', $line_of_text);
// Remove everything before the first "_" and everything after the last "_".
// echo the line
}
fclose($file_handle);
?>
Can someone help me fille in the blanks?
This is what I would do, although a regex might be shorter or more efficient:
$file_handle = fopen("ID.txt", "rb");
while (!feof($file_handle) )
{
$line_of_text = fgets($file_handle);
$parts = explode("\n", $line_of_text);
foreach ($parts as $str)
{
$str_parts = explode('_', $str); // Split string by _ into an array
array_shift($str_parts); // Remove first element
echo current($str_parts)."\n"; // echo current element and newline
// Same as $str_parts[0]
}
}
fclose($file_handle);
Demo: http://codepad.org/uFbVDtbR
Not a big deal, but $lines might be a better variable name there instead of $parts.
If you do need to write this back to the file, you can do this:
ob_start();
// code used above
$new_content = ob_get_clean();
file_put_contents("ID.txt", $new_content);
Relevant references:
http://php.net/manual/en/function.explode.php
http://www.php.net/manual/en/function.array-shift.php
http://php.net/manual/en/function.current.php
http://php.net/manual/en/function.file-put-contents.php
Just use file in a loop
$content = "";
foreach(file("ID.txt", FILE_SKIP_EMPTY_LINES) as $line){
$parts = explode('_', $line);
$content .= $parts[1] . "\n";
}
file_put_contents("ID.txt", $content);
If you want to achieve this by awk,
awk -F _ '{print $2}' ID.txt
Try this
preg_match('/(.*?)(.+?)(.*)/',$line,$matches);
$matches[2] will give the required string
This should work
<?php
// open files
$file_handle = fopen("ID.txt", "rb");
$new_file_handle = fopen("ID2.txt", "wb");
while (!feof($file_handle) ) {
$str = fgets($file_handle);
$start = strpos($str, '_'); // find first "_"
$end = strpos($str, '_', $start + 1); // find next "_"
$newstr = substr($str, $start + 1, $end - $start - 1) . "\n";
fputs($new_file_handle, $newstr);
}
// close files
fclose($file_handle);
fclose($new_file_handle);
// rename
rename("ID2.txt", "ID.txt");
$TXT = file_get_contents(__DIR__.'/in.txt');
$NewTXT = preg_replace('~^.+/[0-9]+_([0-9]+)_.+?$~mi', '$1', $TXT);
file_put_contents(__DIR__.'/out.txt', $NewTXT);
Just rename the .txt files accordingly.

Remove all the line breaks from the html source

Well I know obfuscation is a bad idea. But I want all of my html code to come in one long single line. All the html tags are generated through PHP, so I think its possible. I knew replacing \n\r from regular expression, but have no idea how to do this one. In case I am unclear here is an example
$output = '<p>
<div class="title">Hello</div>
</p>';
echo $output;
To be view in the source viewer as <p><div class="title">Hello</div></p>
Maybe this?
$output = str_replace(array("\r\n", "\r"), "\n", $output);
$lines = explode("\n", $output);
$new_lines = array();
foreach ($lines as $i => $line) {
if(!empty($line))
$new_lines[] = trim($line);
}
echo implode($new_lines);
You can try this perhaps.
// Before any output
ob_start();
// End of file
$output = ob_get_clean();
echo preg_replace('/^\s+|\n|\r|\s+$/m', '', $output);
This should, unless I messed up the regex, catch all output, and then replace all new line characters as well as all whitespace at the end and beginning of lines.
If you already have all output collected in a variable, you can of course just use the last line directly and skip the output buffering stuff :)
Worked for me:
$output = str_replace(array("\r\n", "\r", "\n"), "", $output);
You can do :
$output = '<p>'.
'<div class="title">Hello</div>'.
'</p>';
This way, $output won't contain any line jump.
This should also work :
$output = preg_replace(array('/\r/', '/\n/'), '', $output);
$output = preg_replace('!\s+!m', ' ', $output);
This is already well answered, but you may be able to do more than just trim spaces at both ends of each line:
First extract all text within quotes (you don't want to touch those), replace with a marker with a sequence number, store the sequence number with the text
Extract all text within <script></script> tags and do the same as step #1
Replace all white-space (including \n, \r) with spaces
Replace all >1 space sequences with 1 space
Replace all >_< with >< (_ = space)
Replace all _>, <_ and </_ with >, < and </ (_ = space)
Replace markers with the actual texts
This procedure can potentially compact the entire HTML file. This takes advantage of the fact that multiple white-space text inside HTML tags are intepreted as one single space.
This is a (as far as I have tested) working implementation of Stephen Chung's instructions. I'm not entirely convinced by number five, but have included it anyway.
Put the things you want to protect in the protected_parts array. Do it in order that you want them protected. If the starting and ending bits are different (as they would be in HTML tags), separate them by using a comma.
Also, I've no idea if this is the most optimised way of doing this, but it works for me and seems reasonably fast. Feel free to improve, etc. (Let me know if you do too!)
function MinifyHTML($str) {
$protected_parts = array("<pre>,</pre>", "\"", "'");
$extracted_values = array();
$i = 0;
foreach ($protected_parts as $part) {
$finished = false;
$search_offset = 0;
$first_offset = 0;
$startend = explode(",", $part);
if (count($startend) == 1) { $startend[1] = $startend[0]; }
while (!$finished) {
$first_offset = strpos($str, $startend[0], $search_offset);
if ($first_offset === false) { $finished = true; }
else {
$search_offset = strpos($str, $startend[1], $first_offset + strlen($startend[0]));
$extracted_values[$i] = substr($str, $first_offset + strlen($startend[0]), $search_offset - $first_offset - strlen($startend[0]));
$str = substr($str, 0, $first_offset + strlen($startend[0]))."$#".$i."$".substr($str, $search_offset);
$search_offset += strlen($startend[1]) + strlen((string)$i) + 3 - strlen($extracted_values[$i]);
$i++;
}
}
}
$str = preg_replace("/\s/", " ", $str);
$str = preg_replace("/\s{2,}/", " ", $str);
$str = str_replace("> <", "><", $str);
$str = str_replace(" >", ">", $str);
$str = str_replace("< ", "<", $str);
$str = str_replace("</ ", "</", $str);
for ($i = count($extracted_values); $i >= 0; $i--) {
$str = str_replace("$#".$i."$", $extracted_values[$i], $str);
}
return $str;
}
This is an improved function of the above. It adds text area protection and also anything that is a tag remains untouched.
I also removed strlen in the loop (its static).
This might run faster as a one pass filter to check for any of the protected parts. For such a small protected_parts array it's going to be more efficient than looping through the $str four times.
Also this doesn't fix: class = " " (the extra spaces between = and ") as its stuff inside the tags.
function MinifyHTML($str) {
$protected_parts = array('<pre>,</pre>','<textarea>,</textarea>', '<,>');
$extracted_values = array();
$i = 0;
foreach ($protected_parts as $part) {
$finished = false;
$search_offset = $first_offset = 0;
$end_offset = 1;
$startend = explode(',', $part);
if (count($startend) === 1) $startend[1] = $startend[0];
$len0 = strlen($startend[0]); $len1 = strlen($startend[1]);
while ($finished === false) {
$first_offset = strpos($str, $startend[0], $search_offset);
if ($first_offset === false) $finished = true;
else {
$search_offset = strpos($str, $startend[1], $first_offset + $len0);
$extracted_values[$i] = substr($str, $first_offset + $len0, $search_offset - $first_offset - $len0);
$str = substr($str, 0, $first_offset + $len0).'$$#'.$i.'$$'.substr($str, $search_offset);
$search_offset += $len1 + strlen((string)$i) + 5 - strlen($extracted_values[$i]);
++$i;
}
}
}
$str = preg_replace("/\s/", " ", $str);
$str = preg_replace("/\s{2,}/", " ", $str);
$replace = array('> <'=>'><', ' >'=>'>','< '=>'<','</ '=>'</');
$str = str_replace(array_keys($replace), array_values($replace), $str);
for ($d = 0; $d < $i; ++$d)
$str = str_replace('$$#'.$d.'$$', $extracted_values[$d], $str);
return $str;
}
You can't have <div> inside <p> - it is not spec-valid.
If you don't need to store it in a variable you can use this:
?><div><?php
?><div class="title">Hello</div><?php
?></div><?php

How to detect a delimiter in a string in PHP?

I am curious if you have a string how would you detect the delimiter?
We know php can split a string up with explode() which requires a delimiter parameter.
But what about a method to detect the delimiter before sending it to explode function?
Right now I am just outputting the string to the user and they enter the delimiter. That's fine -- but I am looking for the application to pattern recognize for me.
Should I look to regular expressions for this type of pattern recognition in a string?
EDIT: I have failed to initially specify that there is a likely expected set of delimiters. Any delimiter that is probably used in a CSV. So technically anyone could use any character to delimit a CSV file but it is more probable to use one of the following characters: comma, semicolon, vertical bar and a space.
EDIT 2: Here is the workable solution I came up with for a "determined delimiter".
$get_images = "86236058.jpg 86236134.jpg 86236134.jpg";
//Detection of delimiter of image filenames.
$probable_delimiters = array(",", " ", "|", ";");
$delimiter_count_array = array();
foreach ($probable_delimiters as $probable_delimiter) {
$probable_delimiter_count = substr_count($get_images, $probable_delimiter);
$delimiter_count_array[$probable_delimiter] = $probable_delimiter_count;
}
$max_value = max($delimiter_count_array);
$determined_delimiter_array = array_keys($delimiter_count_array, max($delimiter_count_array));
while( $element = each( $determined_delimiter_array ) ){
$determined_delimiter_count = $element['key'];
$determined_delimiter = $element['value'];
}
$images = explode("{$determined_delimiter}", $get_images);
Determine which delimiters you consider probable (like ,, ; and |) and for each search how often they occur in the string (substr_count). Then choose the one with most occurrences as the delimiter and explode.
Even though that might not be fail-safe it should work in most cases ;)
I would say this works 99.99% of the cases :)
The basic idea is, that number of valid delimiters should be the same line by line.
This script calculates delimiter count discrepancies between all lines.
Less discrepancy means more likely valid delimiter.
Putting it all together this function read rows and return it back as an array:
function readCSV($fileName)
{
//detect these delimeters
$delA = array(";", ",", "|", "\t");
$linesA = array();
$resultA = array();
$maxLines = 20; //maximum lines to parse for detection, this can be higher for more precision
$lines = count(file($fileName));
if ($lines < $maxLines) {//if lines are less than the given maximum
$maxLines = $lines;
}
//load lines
foreach ($delA as $key => $del) {
$rowNum = 0;
if (($handle = fopen($fileName, "r")) !== false) {
$linesA[$key] = array();
while ((($data = fgetcsv($handle, 1000, $del)) !== false) && ($rowNum < $maxLines)) {
$linesA[$key][] = count($data);
$rowNum++;
}
fclose($handle);
}
}
//count rows delimiter number discrepancy from each other
foreach ($delA as $key => $del) {
echo 'try for key=' . $key . ' delimeter=' . $del;
$discr = 0;
foreach ($linesA[$key] as $actNum) {
if ($actNum == 1) {
$resultA[$key] = 65535; //there is only one column with this delimeter in this line, so this is not our delimiter, set this discrepancy to high
break;
}
foreach ($linesA[$key] as $actNum2) {
$discr += abs($actNum - $actNum2);
}
//if its the real delimeter this result should the nearest to 0
//because in the ideal (errorless) case all lines have same column number
$resultA[$key] = $discr;
}
}
var_dump($resultA);
//select the discrepancy nearest to 0, this would be our delimiter
$delRes = 65535;
foreach ($resultA as $key => $res) {
if ($res < $delRes) {
$delRes = $res;
$delKey = $key;
}
}
$delimeter = $delA[$delKey];
echo '$delimeter=' . $delimeter;
//get rows
$row = 0;
$rowsA = array();
if (($handle = fopen($fileName, "r")) !== false) {
while (($data = fgetcsv($handle, 1000, $delimeter)) !== false) {
$rowsA[$row] = Array();
$num = count($data);
for ($c = 0; $c < $num; $c++) {
$rowsA[$row][] = trim($data[$c]);
}
$row++;
}
fclose($handle);
}
return $rowsA;
}
I have the same problem, I am dealing with a lot of CSV's from various databases, which various people extract to CSV in various ways, sometimes different each time for the same dataset ... Have simply implemented a function like this in my convert base class
protected function detectDelimiter() {
$handle = #fopen($this->CSVFile, "r");
if ($handle) {
$line=fgets($handle, 4096);
fclose($handle);
$test=explode(',', $line);
if (count($test)>1) return ',';
$test=explode(';', $line);
if (count($test)>1) return ';';
//.. and so on
}
//return default delimiter
return $this->delimiter;
}
I made something like this:
$line = fgetcsv($handle, 1000, "|");
if (isset($line[1]))
{
echo "delimiter is: |";
$delimiter="|";
}
else
{
$line1 = fgetcsv($handle, 1000, ";");
if (isset($line1[1]))
{
echo "delimiter is: ;";
$delimiter=";";
}
else
{
echo "delimiter is: ,";
$delimiter=",";
}
}
This simply checks whether there is a second column after a line is read.
I am having the same issue. My system will recieve CSV files from the client but it could use ";", "," or " " as delimiter and I wnat to improve the system so the client dont have to know which is (They never do).
I search and found this library:
https://github.com/parsecsv/parsecsv-for-php
Very good and easy to use.

Categories