I need to extract Duration of an "endless" string - php

I have a string like this
aaa ~120 Sek. 53 Sek. ~~ bbb asdfasf aasdf asdfasdf ~600 Sek.~~ ccc ~60 Sek. 43 Sek. ~~ ddd ~240 Sek. 55 Sek. ~~
I have to add up all the xxx Sek. (which are just seconds).
Any good idea?
Regards Bruno

One way to do with regex look ahead, so you can get all the digits followed by Sek word. If you want to do any filtering on a matched result like 3 digits or something else then you can use strlen() function.
$re = '/\d+(?= Sek)/';
$str = 'aaa ~120 Sek. 53 Sek. ~~ bbb asdfasf aasdf asdfasdf ~600 Sek. ~~ ccc ~60 Sek. 43 Sek. ~~ ddd ~240 Sek. 55 Sek. ~~';
preg_match_all($re, $str, $matches, PREG_SET_ORDER, 0);
// Print the entire match result
#print_r($matches);
$sum = 0;
foreach ($matches as $item) {
$sum += $item[0];
}
echo $sum;
Edit: Calculate sum alternatively,
$sum = array_reduce($matches, function(&$result, $item) {
return $result + $item[0];
}, 0);
OR
$sum= array_sum(array_column($matches,0));
Working demo: https://3v4l.org/iQp0q

Related

How Do I Implement Incomplete Answer to Old Question

This comment looks like it would work if the author included the value for $numbers. They say it is some type of array, but don't provide enough information to replicate it. I picture some hard coded array ranging from 0 to 9, but I can't help think that such an array would miss numbers greater than 9. What does the numbers array in this example look like?
$text = "1 out of 23";
if(preg_match_all('/\d+/', $text, $numbers))
$lastnum = end($numbers[0]);
I would just post a comment asking whoever wrote that to paste the value for $numbers, but it says I need reputation points to do that.
See How do I grab last number in a string in PHP?
To answer your initial question print_r() can be used to output all contents of an array. e.g. print_r($numbers)
https://3v4l.org/2jA1b
To explain the code:
\d is a single number
+ is a quantifier meaning one or more of the previous character or group
so this would find all numbers in a string. The $numbers[0] would be all numbers, 1 per index, and the end() pulls to the last number/index. Each index would be a number, the 0 is all matches, each indice at the root level is a capture group.
This code wouldn't work as intended for decimals or comma delimited integers. In those cases the numbers would be split up at the delimiter. 1.0 would become 1 and 0 (2 different numbers).
You could rewrite this as:
$text = "1 out of 23";
if(preg_match('/.*\K\D\d+/', $text, $numbers))
echo $numbers[0];
so the end function is not needed. This pulls everything until the last number then forgets everything before the last number.
What you are trying to do is likely easier using preg_split instead of preg_match_all. We can split the input text by the matched regex (digits) and then rebuild the string while incrementing the numbers as we go.
<?php
function incrementNumbers($text) {
// NOTES:
// parenthesis are important in the regex in order to return the captured values
// the -? will capture negative numbers too if necessary
// PREG_SPLIT_DELIM_CAPTURE allows the captured values to be returned too
$split = preg_split('/(-?\d+)/', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
$return = '';
foreach($split as $i => $s) {
// because we didn't use PREG_SPLIT_NO_EMPTY, $split[0] will either be an empty string if
// $text began with a number, or the text before the first number. Either way, $split alternates
// between non-number [0], number [1], non-number [2], number [3], etc which is why we can detect
// even or odd indexes to determine if this is a number that needs to be incremented or not
if ($i % 2 === 0) {
$return .= $s;
} else {
$return .= (intval($s) + 1);
}
}
return $return;
}
Examples:
echo incrementNumbers("1 out of 23 with 1 and 1 and 24 and 23");
echo incrementNumbers("1 1 2 2 3 3 2 2 1 1");
echo incrementNumbers("0 1 2 3 4 5 6 7");
echo incrementNumbers("-3 -2 -1 0 1 2 3 4 5 6 7");
echo incrementNumbers("there are no numbers in this text");
echo incrementNumbers("does not start 999 with a number 123 nor end 17 with a number");
Outputs:
2 out of 24 with 2 and 2 and 25 and 24
2 2 3 3 4 4 3 3 2 2
1 2 3 4 5 6 7 8
-2 -1 0 1 2 3 4 5 6 7 8
there are no numbers in this text
does not start 1000 with a number 124 nor end 18 with a number
Working example at https://3v4l.org/iKskO

extract the last 8 letters of string with substr

I'm trying to get the last 9 characters of $span.
$span = "";
foreach($html->find('span') as $element1){
if (strpos($element1->outertext, 'kcal') !== false){
$span .= $element1->outertext.'<br>';
}
}
echo substr($span,-9);
It just show me white page, any suggestions?
Edit:
When i debug with var_dump($span) it shows exactly the following:
string(761) " 1 Porsiyon (Orta) AnçuezSardalya Salatası 319 kcal 1 Su Bardağı Ayran (Yağsız) 41 kcal 1 Su Bardağı Anne Sütü 138 kcal 1 Porsiyon (Orta) Amasya Yöresine Özgü Keşkek 728 kcal 1 Porsiyon (Orta) Anne Kurabiyesi 504 kcal "
use trim() to remove white spaces
so you can write
echo substr(trim($span),-9);

Find a number by given digit

Let’s say I have a series of numbers like:
12345678910111213141516... (until unlimited)
Then I would like to get a number from it by given digit. For example:
Digit 10th: 1
Digit 17th: 3
...
I have tried to make the algorithm to do it by using PHP but it always showed me an error due to the looping that I made was out of memory size if the given digit that I gave is more than 10.000.000. Allowed Memory Size of 134217728 Bytes Exhausted
How do I deal with this without having to modify memory_limit on php.ini file?
Here are what I have tried to figure the algorithm out: I benchmark the maximum of upper limit of the loop that my local machine could handle, and I found out it's 10.000.000, then I assumed I need to make a separate loop if the given digit/parameter is more than 10.000.000. But in the end I still got that error of out of memory size. Really grateful in advance.
<?php
/*
* benchmark result:
* max digit = 10.000.000
*/
$benchmarkedDigit = 10000000;
$digit = 1000000000000; // it could be dynamically assigned, i.e. a parameter. In this case will show an error since the given digit is 10 trillion
$s = '';
if ($digit > $benchmarkedDigit) {
$mod = fmod($digit, $benchmarkedDigit);
$div = $digit / $benchmarkedDigit;
for ($x = 1; $x <= $div; $x++) {
$upperLimit = ($x * $benchmarkedDigit);
for ($y = ($upperLimit - $benchmarkedDigit + 1); $y <= $upperLimit; $y++) {
$s .= $y;
}
// so it could be:
// 1 - 10.000.000
// 10.000.001 - 20.000.000
// 20.000.001 - 30.000.000
// ...
}
// loop for the rest of the fmod(), if its result is not 0
for ($i = ($upperLimit + 1); $i <= ($upperLimit + $mod); $i++) {
$s .= $i;
}
} else {
for ($x = 1; $x <= $digit; $x++) {
$s .= $x;
}
}
echo substr($s, ($digit - 1), 1);
You can use the fact that there's always 10^n - 10^(n-1) number of n-digit long numbers (even 1 digit, because I see 0 is not there).
With this knowledge, you can skip potentially huge number of numbers.
You start with n=1, and check if the number of n digit numbers is lower than the desired digit. If it is, then reduce the number of n digit numbers from the desired number, increase n by one and start again.
For example: you want to know the 512th digit in that number
Is the number of 1 digit numbers (10) lower than the desired digit (512)?
Yes, so the desired digit should be reduced by that many (512 - 9).
Is the number of 2 digit numbers (90) lower than the desired digit (503 now)?
Yes, so the desired digit should be reduced by that many (503 - 90).
Is the number of 3 digit numbers (900) lower than the desired digit(413 now)?
No, so the desired digit is one of the digits of a 3 digit number.
413 / 3 is 137 (rounded down), so it's one of the digits of the 137th 3 digit numbers (so 237).
413 % 3 (modulo) is 2, so it's the 2nd digit, so it's supposed to be 3.
There can be miscalculations in this, but the overall logic should not be far.
Edit: you could also use a generator, but this can increase the runtime for big numbers
function getNthDigit() {
for ($i = 0;; ++$i) { // Start with 0, which is the 0-th digit
foreach (str_split((string)$i) as $digit) {
yield $digit;
}
}
}
$desiredDigit = 512;
foreach (getNthDigit() as $number => $digit) {
if ($number == $desiredDigit) {
break;
}
}
// $digit should be the desired digit
<?php
function getDigit($Nth){
if($Nth < 10) return $Nth;
$no_of_digits = 1;
$current_contribution = 9;
$actual_length = 9;
$prev_length = 0;
$starting_number = 1;
$power_of_10 = 1;
while($actual_length < $Nth){
$no_of_digits++;
$current_contribution *= 10;
$prev_length = $actual_length;
$actual_length += ($current_contribution * $no_of_digits);
$power_of_10 *= 10;
$starting_number *= 10;
}
$Nth = $Nth - $prev_length;
$offset = $Nth % $no_of_digits === 0 ? intval($Nth / $no_of_digits) - 1 : intval($Nth / $no_of_digits);
$number = strval($starting_number + $offset);
for($i=1;$i<=$no_of_digits;++$i){
if(($Nth - $i) % $no_of_digits === 0){
return $number[$i-1];
}
}
}
// first 100 Digits
for($i=1;$i<=100;++$i){
echo getDigit($i),PHP_EOL;
}
Demo: https://3v4l.org/3l0I7
Algorithm:
To find the nth digit, we will first find the number and then which digit of that number to choose as an answer.
Find the number:
If we carefully observe, the series increases in a sequential manner, such as shown in the table.
Table:
| Digits| Total numbers(of current digit)| Total Digits | Total digits of whole string |
|-------|--------------------------------|--------------|-------------------------------|
| 1 | 9 | 9 | 9 |
| 2 | 90 | 180 | 189 |
| 3 | 900 | 2700 | 2889 |
| 4 | 9000 | 36000 | 38889 |
The above table shows us that if we want to find, let's say 500th digit, then it's some digit of 3 digit number. If we go for 17th digit, then it's some digit of a 2 digit number and so on.
Now, let's take 200th digit as an example. Since it's less than 2889 and greater than 189, it's from a 3 digit number.
What we would do is breakdown the 200 into a smaller number such as 200 - 189 = 11. This 11 means that it's 11th digit of some 3 digit number which started with initial 3 digit number of 100(the starting number for 3 digit).
Now, we do 11 / 3(where 3 is number of digits) and get the quotient as 3. This 3 means that it's 3 numbers past the starting number 100, which we can say as 100 + 3 = 103(since it's 100,101,102 and then the 4th one as 103).
Now, we came to know that the number is 103. All is left to find out is which digit from 103.
Note that sometimes we come across a corner case of even divisibility such as 12 / 3. In this case, we subtract 1 from the quotient since our series of 3 digits starts from 100 and not 101( and so on and so forth for other digits).
Find out the digit:
Now, we know that the number is 103 for a 200 th digit( a.k.a 11 as we calculated above). To find out which one, we write down numbers of 3 digits in sequence and closely observe them.
Sequence:
1 0 0 1 0 1 1 0 2 1 0 3 1 0 4 1 0 5 1 0 6
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
If you observe, you can understand that the most MSB digit follows a sequence of 1,4,7,10,13 etc. Second most MSB follows a sequence of 2,5,8,11,14 etc and the last MSB(which is LSB) follows a sequence of 3,6,9,12,15 etc.
So, from th above sequence, it's pretty evident that 11(which we got after breaking down 200 initially) belongs to a sequence of the 2nd most MSB digit.
So, the final answer from 103 is 0 (the 2nd digit from left).
$num = '12345678910111213141516';
echo $num[16];
Result: 3

Extract data from a file PHP

I have this array $awstat that I extacted from an awstat file, an withing $awstat I have this that I need:
BEGIN_TIME 24
0 3245 9955 143463426 8047 13601 475741423
1 3122 9131 146244440 7579 12936 507921700
2 2639 5706 95369716 7351 11987 490330698
3 1917 4062 79234871 8245 13009 579453498
4 1757 4263 65580607 7887 11437 454870321
5 1723 4022 44682383 6888 10263 326819624
6 1876 4677 56964771 7339 11242 355385677
7 2796 8473 120152521 7770 12176 362904239
8 4227 13791 196173677 7421 12196 366706352
9 7984 25965 375376297 8398 13883 406545549
10 14605 34418 434054375 7183 13341 380773129
11 15533 41259 559938996 7123 12690 372426426
12 17495 40043 505139834 7432 13402 518541077
13 15815 34170 385108531 6519 12390 396494926
14 16330 41073 508838859 6761 12318 348417806
15 19093 44058 483568307 7692 13583 454365520
16 30429 59672 577852398 8273 13231 473134295
17 25094 48897 478246556 8207 12898 476038603
18 19136 42665 482073005 8087 12983 468300958
19 28849 46228 371229572 7721 12688 471632281
20 14068 30981 341103557 7832 13251 417443822
21 14727 33458 394841797 7575 12644 388811384
22 13480 31364 365096742 7460 13114 411771572
23 7189 19744 272606100 6643 12398 397762547
END_TIME
So I tried this and it doesn't seem to work!
preg_match("/BEGIN_TIME(.*)END_TIME/is", $awstats, $matches);
$time = $matches[0] ;
var_dump($time); // it displays "NULL"
Any solution for this? Thanks!
Not so much an answer, more of an opinion.
Using regular expressions here is overkill.
Something as simple as this will do:
$lines = file("awstats.output");
$lines = array_slice(1); // remove first line
$lines = array_slice(0, -1); // remove last line
foreach ($lines as $line) {
$data = explode(" ", $line);
// handle data
}
You can match linebreaks with:
([^\n]*\n+)+
So this should work:
preg_match("/BEGIN_DAY([^\n]*\n+)+END_DAY/is", $awstats, $matches);
$time = file_get_contents('awstat.log');
$time = preg_replace('/BEGIN_TIME(.*?)END_TIME/sim', '$1', $time);

PHP - Regular Expression - Arrays - Find the last number and add them together

I was given a file that contains something similar to this kind of structure:
12345 ABC 100M 001 2.0 ABC 1010 4510 A01 451 Apple, Johnny A 150
12345 ABC 100M 011 2.0 ABC 1010 4510 A01 451 Apple, Johnny A 80
12345 ABC 100 011 2.0 ABC 1010 4510 A01 451 Apple, Johnny A 80
I need to grab the following sections from this file:
Group together the third column (ie. 100M) if they are similar
Add together the fourth column (if they are in the same group as the third column)
Add up the last column depending on the fourth column
I've managed to do the following:
$List1 = array();
$grab = fopen("file.txt", "r") or die("Can't open file");
$check = fgets($grab);
while(!feof($grab)) {
if (ereg("^[[:digit:]]{5} +ABC +([[:digit:]]{3}[[:alpha:]]?)+ ([[:digit:]]{3})",
$check, $output)) {
if (!in_array($output[1], $List1)) {
array_push($List1, $output[1]);
}
if (!in_array($output[2], $List1)) {
array_push($List1, $output[2]);
}
}
$check = fgets($grab);
}
fclose($grab);
foreach ($List1 as $list) {
print "$list <br/>";
}
I have managed to somehow group together the third column.
The fourth column is being displayed, but I'm not sure how to group it together into the third column if it's under the same group.
And I'm not sure how to easily grab the last bit in the file/array.
Is there a shortcut to getting the last in a file and adding them up?
Thanks in advance for anyone who can help me.
This should do it:
$string = '12345 ABC 100M 001 2.0 ABC 1010 4510 A01 451 Apple, Johnny A 150
12345 ABC 100M 011 2.0 ABC 1010 4510 A01 451 Apple, Johnny A 80
12345 ABC 100 011 2.0 ABC 1010 4510 A01 451 Apple, Johnny A 80';
$third = array();
$fourth = array();
foreach (explode("\n", $string) as $line)
{
// Skip empty lines.
if (empty($line))
continue;
// Clean up any excessive white space.
$line = trim(preg_replace('~[\s]{2,}~', ' ', $line));
$info = explode(' ', $line);
if (!isset($third[$info[2]]))
$third[$info[2]] = array();
$third[$info[2]][] = $info;
if (!isset($fourth[$info[3]]))
$fourth[$info[3]] = 0;
$fourth[$info[3]] += (int) end($info);
}
print_r(array(
'third' => $third,
'fourth' => $fourth,
));

Categories