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

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,
));

Related

Parsing input text to array and sum values

Hello :) I am stuck in my mini-app developing. I have following text, copied from 3rd party web page:
Type A GZ 600 11.09.2021 12:00 OST 9
Type A GZ 601 11.09.2021 13:20 ADS 1
Type A GZ 602 11.09.2021 21:35 OCS 1
Type A GZ 603 11.09.2021 14:50 CSE 10
Type B GZ 600 11.09.2021 12:00 OST 5
Type B GZ 601 11.09.2021 13:20 ADS 3
Type B GZ 602 11.09.2021 21:35 OCS 6
Type B GZ 603 11.09.2021 14:50 CSE 12
I need to parse it to following format:
$s = 10, $ns = 11, $bs = 26, like:
echo "S:" . $s . " NS:" . $ns . " BS:" . $bs; // Output: S:10 NS:11 BS:26
where:
$fa = array("OCS", "CSE"); is array of codes
$ns is sum of Type A last column numbers, which 5 column 3-letter code is in the array,
$s is sum of Type A last column numbers, which 5 column 3-letter code is not in the array
$bs is just sum of Type B last column numbers
My code now is following:
if(!empty($_POST['indata'])){
$in_data = $_POST['indata']; // Get POST data
$fa = array("OCS", "CSE"); // Make array
$ns = 0; // Init ns value
$s = 0; // Init ss value
foreach(explode("/n",$in_data) as $line){ // Divide text to lines
$info[] = explode(" ", $line); // Divide line to values and put them to array
print_r($info); //Show input for test purposes
if(in_array($info[4], $fa)) { // Check, if 4th array value (code) is in array
$ns = $ns + $info[5]; // plus to $ns, if yes
} else {
$s = $s + $info[5]; // plus to $s, if no
}
unset($info); // clear array for next usage
}
}
But it seems not cutting line into array. It just shows me lines, not dividing to array. I am using Summernote text editor, it sends data as rows.
Because you're using $info[] = ... you get a 2 levels deep array instead of 1 level as your code is expecting. $info[] = ... basically means "Add the right hand side to $info as one element". So if the right hand side is a string and $info was empty before you'd get [0 => "my string"]. If the right hand side was an array you'd get [0 => [0 => "my", 1 => "array"]].
Do you see what I am getting at? Your code is adding one element to $info, never more than that. So to access anything in $info the first part needs to be $info[0]. But the code looks for the 4th and 5th elements, and they'll never be there. On the other hand, if you'd look for the 4th element inside the 1st one.. That is, $info[0] for the 1st element, and then the 4th inside it: $info[0][4], then you get what you're looking for.
if(!empty($_POST['indata'])){
$in_data = $_POST['indata']; // Get POST data
$fa = array("OCS", "CSE"); // Make array
$ns = 0; // Init ns value
$s = 0; // Init ss value
foreach(explode("\n",$in_data) as $line){ // Divide text to lines
$info[] = explode(" ", $line); // Divide line to values and put them to array
if(in_array($info[0][4], $fa)) { // Check, if 4th array value (code) is in array
$ns = $ns + (int) $info[0][5]; // plus to $ns, if yes
} else {
$s = $s + (int) $info[0][5]; // plus to $s, if no
}
unset($info);
}
}
var_dump($ns, $s); // int(29) int(18)
Version 2. Do away with one level in $info as mentioned earlier:
foreach(explode("\n",$in_data) as $line){
$info = explode(" ", $line);
if(in_array($info[4], $fa)) {
$ns = $ns + (int) $info[5];
} else {
$s = $s + (int) $info[5];
}
}
Alternative version, regexp:
foreach(explode("\n",$in_data) as $line){
$info = preg_split('/\s{4,}/', $line); // Split when 4 or more spaces
if(in_array($info[3], $fa)) {
$ns = $ns + (int) $info[4];
} else {
$s = $s + (int) $info[4];
}
}
That way you don't get any "junk columns" :).
Edit: I think it was PHP 7.1 that introduced some more "strictness" regarding adding values of different types, strings + numbers that is. A notice is issued, "A non well formed numeric value encountered". But if the string is cast/converted as a number before summing PHP will accept it. Casting can be done by adding (int) in front of the string value. (Provided it contains an integer value, of course, otherwise it needs to be cast differently)
After parsing the lines of text with regex, you only need to iterate the matches and conditionally sum the groups.
Code: (Demo)
$text = <<<TEXT
Type A GZ 600 11.09.2021 12:00 OST 9
Type A GZ 601 11.09.2021 13:20 ADS 1
Type A GZ 602 11.09.2021 21:35 OCS 1
Type A GZ 603 11.09.2021 14:50 CSE 10
Type B GZ 600 11.09.2021 12:00 OST 5
Type B GZ 601 11.09.2021 13:20 ADS 3
Type B GZ 602 11.09.2021 21:35 OCS 6
Type B GZ 603 11.09.2021 14:50 CSE 12
TEXT;
$fa = ["OCS", "CSE"];
$result = ['s' => 0, 'ns' => 0, 'bs' => 0];
preg_match_all(
'/^Type ([AB]).+([A-Z]{3})\h+(\d+)$/m',
$text,
$matches,
PREG_SET_ORDER
);
foreach ($matches as $m) {
if ($m[1] === 'B') {
$result['bs'] += $m[3];
} elseif (in_array($m[2], $fa)) {
$result['s'] += $m[3];
} else {
$result['ns'] += $m[3];
}
}
var_export($result);
Output:
array (
's' => 11,
'ns' => 10,
'bs' => 26,
)

PHP: filter specific pattern out of string

My raw output of socket_recvfrom is:
ID IP PING IDENTIFIERNUMBER USERNAME
0 127.0.0.1:1234 0 ID123456789 Moritz
1 127.0.0.1:1234 46 ID123456789 August Jones
2 127.0.0.1:1234 46 ID123456789 Miller
It is a single string that contains all of this informations in once and just contains whitespaces between the informations. All keys can be longer or shorter.
My problem:
When I preg_split("/\s+/") it, then I get a good array with useable data, but when the username contains spaces it creates a second index for this. Not good, all data that comes after this just get destroyed.
I sort the array like this: ID, USERNAME, PING, IDENTIFIERNUMBER, IP
Example by the sorting output with username with one space in it:
ID: 0, USERNAME: Moritz, PING: 0, IDENTIFIERNUMBER: ID123456789, IP: 127.0.0.1:1234
ID: 1, USERNAME: August, PING: Jones, IDENTIFIERNUMBER: 46, IP: ID123456789
ID: 127.0.0.1:1234, USERNAME: 2, PING: Miller, IDENTIFIERNUMBER: 46, IP: ID123456789
How do I get the information correctly out of the string?
Just forgot to say:
The string begins with: --------------------------------- in a not countable order. So it can be like 10 characters or 12.
The string ends with:
(8 users in total)
The regex methode looks good. I only need to filter out the other characters.
--------------------------------- 0 127.0.0.1:1234 0 ID123456789(OK) Moritz 1 127.0.0.1:1234 46 ID123456789(OK) August Jones 2 127.0.0.1:1234 46 ID123456789(OK) Miller (7 users in total)
Last problem:
https://www.regex101.com/r/wP8cW1/1
You may use regex
(?P<ID>\d+)\s+(?P<IP>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+)\s(?P<PINGR>\d+)\s(?P<IDENTIFIERNUMBER>ID\d+)(\(OK\))?(?P<USERNAME>(\s[A-z]\w+)+)
MATCH 1
ID [0-1] `0`
IP [2-16] `127.0.0.1:1234`
PINGR [17-18] `0`
IDENTIFIERNUMBER [19-30] `ID123456789`
USERNAME [31-37] `Moritz`
MATCH 2
ID [39-40] `1`
IP [41-55] `127.0.0.1:1234`
PINGR [56-58] `46`
IDENTIFIERNUMBER [59-70] `ID123456789`
USERNAME [71-83] `August Jones`
MATCH 3
ID [85-86] `2`
IP [87-101] `127.0.0.1:1234`
PINGR [102-104] `46`
IDENTIFIERNUMBER [105-116] `ID123456789`
USERNAME [117-123] `Miller`
Demo and explanation
Do you alredy try explode the string by new lines \n ??
test this code.
$str = '0 127.0.0.1:1234 0 ID123456789 Moritz
1 127.0.0.1:1234 46 ID123456789 August Jones
2 127.0.0.1:1234 46 ID123456789 Miller';
$lines = array_filter(explode("\n", $str));
foreach ($lines as $value) {
$t[] = preg_split("/\s+/", trim($value));
}
Now in the var $t you have a usefull data.

Multiple comparison and edit file

Here is my $file1 structure (there are thousands of like that groups):
Group id_7653
{
type register
sub_name 155
1 3123 1 12
2 3124 1 8
3 3125 1 4
4 3126 1 12
5 3127 1 8
6 3128 1 4
.....
}
Group id_8731
{
type register
sub_name 155
1 4331 1 12
2 4332 1 8
3 4333 1 4
4 4334 1 12
5 4335 1 8
6 4336 1 4
.....
}
And here is my $file2 structure (again, there are thousands of defined values)
.....
3123 Spada+1
3124 Spada+2
3125 Spada+3
3126 Spada+4
3127 Spada+5
3128 Spada+6
3129 Spada+7
3130 Spada+8
.....
And here is my Worker script that makes, compares $file1 and $file2.
<?php
//read the first file in as a string
$file1 = file_get_contents("dataparser\names1.txt");
//read the second file in as an array
$file2 = file("dataparser\names2.txt");
//index from file2 that we are going to build
$file2Index = array();
foreach($file2 as $line){
//split the line
$line = explode("\t", $line, 2);
//validate the line, should be only 2 values after explode and first should be a number
if(count($line) == 2 && is_numeric($line[0])){
//add to index
$file2Index[$line[0]] = $line[1];
}
}
//now get all the values from file1 that we want (second column)
preg_match_all('/^\s*\d+\s*(\d+)\s*\d+\s*\d+\s*$/m', $file1, $matches);
$file1Values = array_unique($matches[1]);
//loop over the matches from column 2
foreach($file1Values as $value){
//check if the key doesn't exist
if(!isset($file2Index[$value])){
//echo error message
echo "Value {$value} does not exist in file2<br>";
}
}
?>
What makes that script:
Compares $file1 and $file2 and shows me which values are not defined in $file2
So far, everything works okay.
I want to extend that my script a little bit, so I want to replace that {$value} with my $file2 structure.
This time, I don't want to check that value, I want replace it directly from $file1 value. (Spada etc...)
Which paths I should follow...? Can I get some examples please...

Reading content from positional text file in PHP?

I have text file having the following format
00151422 N8X 3V6 2013-11-11 00:19:00.000 IN patricksoong#hotmail.com E 200-2462 Howard Avenue Windsor ON CAN N8X3V6 M Dr. Patrick Soong
00331448 T6E 2R1 2010-03-01 00:00:00.000 IN atirlea#yahoo.com E 9743 88 Ave NW Edmonton AB CAN T6E2R1 Alina Tirlea Engstrom
00364578 K7N 1A3 2011-01-12 00:00:00.000 IN E 4463 Bath Rd Amherstview ON CAN K7N1A3 M Mr. Martin Kandler
The above positional text file contains 3 records and 20 fields in each record. Also I now the size for each column. How will i read records and fields with in a record using PHP?
Size of fields are
f1=8;f2=10;f3=10;f4=10;f5=255;f6=50;f7=255;f8=10;f9=10;f10=50;f11=50;f12=1;f13=20;f14=50;f15=50;f16=60;f17=10;f18=20;f19=20;f20=1;
Use a substr() inside some kind of loop. Untested, but should give you an idea:
$lengths = [8,10,10]; // define all the lengths
function fixed_width_data_to_array($data, $lengths) {
foreach($rows as $row) {
$position = 0; // start at the beginning of the row
foreach($lengths as $length) {
// add current field to array
$my_data[] = trim(substr($row, $position, $length));
// move the 'pointer' to the start of the next field
$position += $length;
}
// add current row to an array
$my_array[] = $my_data;
}
return $my_array;
}

PHP Regex correct syntax preg_split after any number of words and 1-3 digits

I'm trying to break down a rss feed with sports scores
Example data
San Diego 4 Chicago Cubs 2
Miami 2 Philadelphia 7
Boston 3 Toronto 1
Washington 3 Atlanta 1
Chicago Sox 3 Texas 1
St. Louis 6 Milwaukee 5
The rss basically gives me one flowing string like San Diego 4 Chicago Cubs 2 and i'm trying to break it down for better use.
Basically im trying to first split San Diego 4 Chicago Cubs 2 into four variables, $home_team, $home_score, $away_team, $away_score.
But, obviously the home team could be one word or more, the score could be 1 digit or up to 3 so i've been trying to figure out the best regular expression to split this up in the correct format.
Does anyone have any ideas?
Update
Code that i'm actually using this for, i'm pulling xml of mlb games today, filtering out just the games that are labeled as Final meaning Final Score and then im trying to break it down further from there..
<?php
$xml = simplexml_load_file("http://feeds.feedburner.com/mpiii/mlb?format=xml");
foreach($xml->channel->item as $item){
if(preg_match('/(FINAL)/', $item->title, $matches) || preg_match('/(POSTPONED)/', $item->title, $matches)){
if(preg_match('/(POSTPONED)/', $item->title, $matches)){
continue;
}
$string = $item->title;
$patterns = array();
$patterns[0] = '/\\(FINAL\\)/';
$patterns[1] = '/\\(POSTPONED\\)/';
$replacements = array();
$replacements[1] = '';
$replacements[0] = '';
$string = preg_replace($patterns, $replacements, $string);
$keywords = preg_match("^(.*?) ([0-9]{1,3}) (.*?) ([0-9]{1,3})$", $string);
echo $keywords[1]."<br/>";
}
}
?>
You can split the string based on a sequence of digits, assuming that team names don't contain digits as well :)
$s = 'San Diego 4 Chicago Cubs 2';
list($home_team, $home_score, $away_team, $away_score) = array_filter(
array_map('trim',
preg_split('/\b(\d+)\b/', $s, -1, PREG_SPLIT_DELIM_CAPTURE)
), 'strlen');
$arr = array("San Diego 4 Chicago Cubs 2",
"Miami 2 Philadelphia 7",
"Boston 3 Toronto 1",
"Washington 3 Atlanta 1",
"Chicago Sox 3 Texas 1",
"St. Louis 6 Milwaukee 5"
);
$results = array();
foreach ($arr as $v) {
$scores = preg_split("/[A-Za-z\s\.]+/", $v);
$teams = preg_split("/[\d]+/", $v);
$results[] = "Home: ".$teams[0]." (".$scores[1]."), Away: ".$teams[1]." (".$scores[2].")"; }
foreach ($results as $v) {
echo $v."<br>"; }
Results:
Home: San Diego (4), Away: Chicago Cubs (2)
Home: Miami (2), Away: Philadelphia (7)
Home: Boston (3), Away: Toronto (1)
Home: Washington (3), Away: Atlanta (1)
Home: Chicago Sox (3), Away: Texas (1)
Home: St. Louis (6), Away: Milwaukee (5)
You could obviously construct $results however you wish; but the meat of the solution is the regexes:
$scores = preg_split("/[A-Za-z\s\.]+/", $v);
$teams = preg_split("/[\d]+/", $v);
Maybe
<?php
$rssLine="San Diego 4 Chicago Cubs 2";
//add code to loop though lines
if(preg_match ("/^(.*?) ([0-9]{1,3}) (.*?) ([0-9]{1,3})$/" ,$rssLine, $matches) ===1){
$home_team = $matches[1];
$home_score = $matches[2];
$away_team = $matches[3];
$away_score = $matches[4];
}
else{
//log no match found
}
?>
Match 1 is home team. Match 2 is home score. Match 3 is away team. Match 4 is away score
This might be exactly what you want:
<?php
$your_input_string ="San Diego 4 Chicago Cubs 2 Miami 2 Philadelphia 7 Boston 3 Toronto 1 Washington 3 Atlanta 1 Chicago Sox 3 Texas 1 St. Louis 6 Milwaukee 5 ";
$your_result = array_chunk(array_filter( array_map('trim', preg_split('/\b(\d+)\b/', $your_input_string, -1, PREG_SPLIT_DELIM_CAPTURE)), 'strlen'),4);
echo '<pre>';
print_r($your_result);
?>
Live Demo Here>>

Categories