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.
Related
I'm need an array of all the folders in a directory, im using laravel to get an array of folders in the directory using the below code.
Storage::allDirectories('/public');
this returns an array like below.
$directories = [
"Gallery",
"Images",
"Images/Proof",
"Images/Proof/Another",
];
I need a way to remove duplicate parent directories, so for example Images will be remove if Images/Proof/Another exists or something like that, i just need to build an array of a directory and its subfolders as one array, if that makes sense.
so for example:
$directories = [
["Gallery"],
["Images/Proof/Another"],
];
Or something like this, I can't think of a way to get this to work.
I'm building a custom media manager component if anyone was wondering, some exist but i have no control over them so i built my own.
all help is appreciated.
Can you post the code you have tried so far
For 1 i can't get my head around the logic, thats why im asking what i've done "Which i dont see how it would help at all"
foreach (Storage::allDirectories('/public') as $folder)
{
$all_folders[] = $exploded('/',$folder);
}
Im trying to separate the array into smaller arrays and check each exploded bit against another. But this has me running in circles.
Do like below (Easiest way):-
$final_array = [];
foreach($directories as $dir){
$final_array[explode('/',$dir)[0]] = $dir;
}
$final_array =array_values($final_array);
Output:- https://eval.in/912417
Or:-
$final_array = [];
foreach($directories as $dir){
$final_array[explode('/',$dir)[0]][0] = $dir;
}
$final_array =array_values($final_array);
Output:-https://eval.in/912418
If you rsort (descending) it and foreach it you can then use preg_grep to see if it exsists in your new array.
If not add it.
$directories = [
"Gallery",
"Images",
"Images/Proof",
"Images/Proof/Another",
];
Rsort($directories);
$new=[];
Foreach($directories as $val){
If(!preg_grep("~" . $val. ".*~",$new)){
$new[] = $val;
}
}
Var_dump($new);
https://3v4l.org/0EnZ3
Preg_grep will look for the pattern and see if it exsists in the array.
Since I loop descending it will first look at Images/Proof/Another then add it to the list because it's not there.
Next iteration will look at Images/Proof/ and since preg_grep has pattern Images/Proof/.* it will be true thus not add it in the list.
Then the same with images.
You can just filter array using array_filter function.
<?php
$directories = [
"Gallery",
"Images",
"Images/Proof",
"Images/Proof/Another",
];
$filtered = array_filter($directories, function($v) use ($directories) {
foreach($directories as $dir) {
if(strpos($dir, $v . "/") === 0) {
return false;
}
}
return true;
});
var_dump($filtered);
Not so elegant, but clear to read.
Quick and dirty, but tested and works
function removeRedundantParents($directories)
{
foreach ($directories as $key => &$val)
{
foreach ($directories as $i => &$v)
{
if ($val !== $v && substr($v, 0, strlen($val)) == $val)
{
unset($directories[$key]);
continue;
}
}
}
return $directories;
}
I achieved this by removing the row if it satisfies the logic.
Mutative, but satisfies the requirement correctly.
Tested, works properly.
check out https://eval.in/912439 for the snippet and output
Code
$directories = array_flip($directories);
foreach ($directories as $dir => $key) {
$parent_dir = preg_match('/(.*)\//', $dir, $match);
if (isset($match[1])) {
unset($directories[$match[1]]);
}
}
$directories = array_keys($directories);
I have a file called "single.txt". The contents look like:
Array ( [0] => Ada Lovelace,F,96,ISTJ,Linux,24,99
[1] => Adele Goldberg,F,65,ENFJ,Windows,50,70
[2] => Alan Turing,M,41,ESTP,Mac OS X,31,50...
)
First, when a new person signs up, it adds them with them with their info to the end of the .txt file. I want to be able to check whether they've already signed up and I've written the following function:
function returnPerson($content){
global $person_name;
for($i=0 ; $i < count($content); $i++){
if($person_name == array_slice($content,0,0)){
$person = $content[$i];
return $person;
} else continue;
}
}
But that doesn't seem to be working.
How can I compare the first part of the string, i.e. the name part, to the name of the person checking?
Thanks!
Try something like this... you may have to modify it slightly depending on how your text is coming in, but should get you on the right track :)
function returnPerson($content){
global $person_name;
foreach($content as $profile) {
$profile = explode(',', $profile);
if ($person_name == $profile[0]) {
// Person Exists
return $profile;
}
}
return false; // person does not exist
}
You're "slicing" the same array while you're looping it. It looks like you just need a simple strpos():
if(strpos($content[$i], $person . ',') === 0){
return ...
}
Here's another way that doesn't require a for loop:
$names = array_map('reset', array_map('str_getcsv', $content));
if(in_array($person, $names)){
...
}
It works because your data seems to use the CSV format
You can loop over the elements in the array like this:
foreach ($content as $record) {
// $record now contains string "Ada Lovelace,F,96,ISTJ,Linux,24,99"
}
You can extract fields from a comma-separated string by using the explode() function:
$string = "Ada Lovelace,F,96,ISTJ,Linux,24,99";
$fields = explode(',', $string);
// $fields[0] now contains "Ada Lovelace"
// $fields[1] now comtains "F"
// ... etc
Putting those together, you'll get something like:
foreach ($content as $record) {
$fields = explode(',', $record);
if ($fields[0] == $name_to_check) {
// found it
}
}
// didn't find it
I have two arrays like this
$slike = Array('artist2-1.jpg', 'artist2-2.jpg', 'artist2-3.jpg', 'artist2-4.jpg');
$slikethumb = Array('artist2-1_thumb.jpg',
'artist2-2_thumb.jpg',
'artist2-3_thumb.jpg',
'artist2-4_thumb.jpg');
When I foreach two arrays I want to get this output on view
echo'<a href='.$slike.'><img src='.$slikethumb.'></a>';
I know how to foreach one array, but what I have to do to combine two arrays in one foreach?
If those arrays are sorted correctly and both have same number of elements:
foreach($slike as $i => $value){
echo'<a href='.$value.'><img src='.$slikethumb[$i].'></a>';
}
Edit:
Considering your related question, You might want to think about scanning your directoy using glob and a more specific pattern, something along the lines of:
$sLike = glob('{0,1,2,3,4,5,6,7,8,9}.jpg',GLOB_BRACE);
//selects all .jpg files with names ending with number.jpg, so no _thumb.jpg files
Then, with this in mind and in light of your comment. How to remove the file extensions:
$sLike = array();//the array from dir scan
$clean = array();
foreach($sLike as $file)
{
$clean[] = substr($file, 0, strpos($file, '.')*-1);//get file name
}
$format = '<img src="%1$s_thumb.jpg" />';//output format
foreach($clean as $like)
{
printf($format, $like);
}
Or, with a more liberal pattern used for the glob call, and if you want to go with option 2 (the one with different extensions per file):
$sLike = array();//the array from dir scan
$clean = array();
foreach($sLike as $file)
{
$endOffset = strpos($file, '.')*-1;
$clean[] = array(
substr($file, 0, $endOffset),//get file name
substr($file,$endOffset)//get extension, includes the . char!
);
}
$format = '<img src="%1$s_thumb%2$s" />';//output format
foreach($clean as $like)
{
echo vsprintf($format, $like);
}
That's about it...
What I'd do is use but a single array. The base string is the same for both images:
$slike=array(
'artist2-1',
'artist2-2',
'artist2-3',
'artist2-4'
);//remove extensions
$format = '<img src="%1$s_thumb.jpg" />';//output format
foreach($slike as $like)
{
printf($format, $like);
}
That will give you what you want, without there ver being a need for 2 arrays, as you can see on this codepad.
Benefits of this approach:
only constructs 1 array, not 2
uses printf which translates internally to a fprintf(stdout, "<your format>", zval*$like); in C, which is fast.
No excess fat: there's very little repetition. You could even change the format to artist2-%s.jpg"><img src="artist2-%s_thumb.jpg" />, and only stores the numbers in the array, but that's taking it absurdly far.
Short, and concise code
Of course, there are caveats:
less readable & maintainable code
If ever you add 1 image with a different extension (png), you'll have to refactor...
The choice is yours. If different extensions is likely to happen, you could change the code above to something like:
$slike=array(
array('artist2-1','jpg'),
array('artist2-2','png'),
array('artist2-3','gif'),
array('artist2-4','jpeg'),
);//remove extensions
$format = '<img src="%1$s_thumb.%2$s" />';//output format
foreach($slike as $like)
{
echo vsprintf($format, $like);
}
As you can see on this codepad, that'll work like a charm, too
If same number of elements in both array & sorted then,
foreach($slikethumb as $key => $val) {
echo '<a href='.$slike[$key].'><img src='.$val.'></a>';
}
You can use for loop
for($i = 0; $i< count($slike); $i++)
{
$slike_val = $slike[$i];
$slikethumb_val = $slikethumb[$i];
echo'<a href='.$slike_val.'><img src='.$slikethumb_val.'></a>';
}
You will try
foreach($slike as $key=>$slike_val)
echo'<a href='.$slike_val.'><img src='.$slikethumb[$key].'></a>';
Try like this:
<?php
$slike = array('artist2-1.jpg', 'artist2-2.jpg', 'artist2-3.jpg', 'artist2-4.jpg');
$slikethumb = array('artist2-1_thumb.jpg', 'artist2-2_thumb.jpg', 'artist2-3_thumb.jpg', 'artist2-4_thumb.jpg');
foreach($slike as $key1 => $value1)
{
echo $value1."--------->".$slikethumb[$key1]."<br />";
}
?>
Thanks
foreach ($slike as $key=>$img) {
print '<a href='.$img.'><img src='.$slikethumb[$key].'></a>';
}
Or if you want one which is not based on the ordering....
foreach ($slike as $key=>$img) {
list($name, $ext)=explode('.', $img);
$thumb=$name . '_thumb.' . $ext;
if (in_array($thumb, $slikethumb)) {
print '<a href='.$img.'><img src='.$thumb.'></a>';
} else {
...?
}
}
I have a simple text file (see below).
abc|1356243309
zzz|1356239986
yyy|1356242423
I want to simply extract all names before |. So the output should look like:
abc
zzz
yyy
Below is the string I've attempted to use. I've also tried file('visitornames.txt') etc. I'm not sure what im doing wrong :(. Tried a lot of things.
$string = file_get_contents('visitornames.txt');
$names = explode('|', $string);
foreach($names as $key) {
echo $key . '"<br/>';
}
No errors, but its simply not doing it correctly.
I would use file-function instead of file_get_contents. It returns file as array where every line is own item. Then you can foreach file content array. Variable you are looking for is first part of the line and that's why you can just echo $names[0].
$file = file('visitornames.txt');
foreach ($file AS $line) {
$names = explode('|', $line);
echo $names[0] . '<br/>';
}
You can extract all lines of a file with the file function, then take each first part of the explode and assign it to the result:
$names = array();
foreach (file('visitornames.txt') as $line)
{
list($names[]) = explode('|', $line, 2);
}
Alternatively there is SplFileObject that is similar but you can more easily extend from it:
class Names extends SplFileObject
{
public function current() {
list($name) = explode('|', parent::current(), 2);
return $name;
}
}
$names = new Names('visitornames.txt');
foreach ($names as $name)
{
echo $name, "<br />\n";
}
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.