In a text file, I have the folowing strings :
ID | LABEL | A | B | C
--------------------------------------
9999 | Oxygen Isotopes | | 0.15 | 1
8733 | Enriched Uranium | | 1 | 1
I would like to extract the fields ID and LABEL of each line using regular expression.
How I can achieve it ?
I am not certain why you insisted on regex.
As the column appear to be separated by | symbol, it seems like using PHP function explode would be an easier solution.
You would be able loop through the lines, and refer to each column using typical array index notation, for example: $line[0] and $line[1] for ID and LABEL respectively.
I doubt regex is the best solution here.
Try this to separate the text file into an array of lines (this might or might not work, depending on the OS of the machine you created the txt file on)
$lines = explode($text, "\n");
$final_lines = array();
foreach ($lines as $line) {
$parts = explode($line, " | ");
$final_lines[] = $parts;
}
Now you can access all of the data through the line number then the column, like
$final_lines[2][0]
Will contain 8733.
You could use preg_split on every line:
$array = preg_split(`/\s*\|\s*/`, $inputLine, 2);
Then as in djdy's answer, the ID will be in $array[0] and the label in $array[1].
No regex needed:
<?php
$file = file('file.txt');
$ret = array();
foreach($file as $k=>$line){
if($k<2){continue;}
list($ret['ID'][],
$ret['LABEL'][],
$ret['A'][],
$ret['B'][],
$ret['C'][]) = explode('|',$line);
}
print_r($ret);
//Label: Oxygen Isotopes ID:9999
echo 'Label: '.$ret['LABEL'][0].' ID:'.$ret['ID'][0];
/*
Array
(
[C] => Array
(
[0] => 1
[1] => 1
)
[B] => Array
(
[0] => 0.15
[1] => 1
)
[A] => Array
(
[0] =>
[1] =>
)
[LABEL] => Array
(
[0] => Oxygen Isotopes
[1] => Enriched Uranium
)
[ID] => Array
(
[0] => 9999
[1] => 8733
)
)
*/
?>
Regular expressions might not be the best approach here. I'd read in each line as a string, and use String.explode("|", input) to make an array of strings. The 0 index is your ID, the 1 index is your label, and so on for A, B, and C if you want. That's a more robust solution than using regex.
A regular expression that gets the ID might be something like
\d{4} |
You could do something similar for the label field, bug again, this isn't as robust as just using explode.
Though its not a best approach to use regular expression here but one may be like this
preg_match_all("/(\d{4}.?)\|(.*?)\|/s", $data, $matchs)
2nd and 3rd index of $matches will carry the required data
Try
$str = file_get_contents($filename);
preg_match_all('/^\s*(\d*)\s*\|\s*(.*?)\s*\|/m', $str, $matches);
// $matches[1] will have ids
// $matches[2] will have labels
Related
I'm wanting to replace the first character of a string with a specific character depending on its value,
A = 0
B = 1
C = 2
Is there a way to do this based on rules? In total I will have 8 rules.
Ok, so I'm editing this to add more information as I don't think some people understand / want to help without the full picture...
My string will be any length between 5 and 10 characters
Capitals will not factor into this, it is not case sensitive
Currently there is no code, I'm not sure the best way to do this. I can write an if statement on a substring, but I know straight away that is inefficient.
Below is the before and after that I am expecting, I have kept these examples simple but all I am looking to do is replace the first character with a specific character depending on its value. For now, there are eight rules, but this could grow in the future
INPUT OUTPUT
ANDREW 1NDREW
BRIAN 2RIAN
BOBBY 2OBBY
CRAIG 3RAIG
DAVID 4AVID
DUNCAN 4UNCAN
EDDIE 5DDIE
FRANK 6RANK
GEOFF 7EOFF
GIANA 7IANA
HAYLEY 8AYLEY
So as you can see, pretty straight forward, but is there a simple way to specifically specify what a character should be replaced by?
Assuming all the rules are for single characters, like in the example, it would be easisest to code them in to a dictionary:
$rules = array('A' => 0, 'B' => 0 /* etc... */);
$str[0] = $rules[$str[0]];
I think this is what you want.
<?php
$input = array('ANDREW','BRIAN','BOBBY','CRAIG','DAVID','DUNCAN','EDDIE','FRANK','GEOFF','GIANA','HAYLEY');
$array = range('A','Z');
$array = array_flip(array_filter(array_merge(array(0), $array)));
$output = [];
foreach($input as $k=>$v){
$output[] = $array[$v[0]].substr($v, 1);
}
print_r($output);
?>
Output:
Array (
[0] => 1NDREW
[1] => 2RIAN
[2] => 2OBBY
[3] => 3RAIG
[4] => 4AVID
[5] => 4UNCAN
[6] => 5DDIE
[7] => 6RANK
[8] => 7EOFF
[9] => 7IANA
[10] => 8AYLEY
)
DEMO: https://3v4l.org/BHLPk
I have a table named 'traffic' and two columns 'line' (there are over 50 lines) and 'vehicle' (over 300). This table contains a list of all of the lines and the vehicle they depend on. Each line has several vehicles and I need to group them (vehicles) in columns by line.
line | vehicle
______________________
line_a | veh1
line_a | veh12
line_a | veh123
line_b | veh14
line_b | veh15
line_b | veh16
line_c | veh17
line_c | veh18
Expected Output
line_a | line_b | line_c | .... to line 50
______________________________________
veh1 veh14 veh17
veh12 veh15 veh18
veh123 veh16
Any way to do this?
load all to php arrays
iterate once and collect unique lines via $helparr[$line]=array(), but also collect values per line array_push($helparr[$line],$vehicle)
iterate again: for all $helparr (output $line, for all $helparr[$line] (output $vehicle))
...guess, half of your coding work is done now...
first of all you need to fetch all the records from your database,
here i'm using a demo array $input just for example, you do not need this array when you are fetching from database, you can start from foreach
<?php
$input = array(0=>array('line'=>'line_a','vehicle'=>'veh1'),
1=>array('line'=>'line_b','vehicle'=>'veh2'),
2=>array('line'=>'line_c','vehicle'=>'veh6'),
3=>array('line'=>'line_a','vehicle'=>'veh5'),
4=>array('line'=>'line_c','vehicle'=>'veh9')
);
$newArr =array();
foreach($input as $key =>$val)
{
$newArr[$val['line']][] = $val['vehicle'];
}
echo "<pre>"; print_r($newArr);
?>
your $newArr looks like this
Array
(
[line_a] => Array
(
[0] => veh1
[1] => veh5
)
[line_b] => Array
(
[0] => veh2
)
[line_c] => Array
(
[0] => veh6
[1] => veh9
)
)
your vehicles are sorted according to their respective lines and you can use $newArray to print according to your need.
I hope this will work for you!
In a school work, I built a site for a fictional space museum in my city using PHP. It has a Data Inclusion and Data Consultation systems, but I have a problem with the consultation that I want to know how to solve: how to delete the last line break from the file?
Data Inclusion
In the restricted area of the site, I have a HTML5 form with 5 fields (name, addres, telephone number, sex and visited exhibitions) that sends the data by the method POST to a function in PHP that writes it on a given txt file by using the fwrite command:
fwrite ($pointer, "$name | $addres | $telephone | $sex | $exhibitions " .PHP_EOL);
As you can see, it writes in a txt file the data entered on the form, plus a line break. The pointer is the variable used by fopen to open the file that I need to work. Example of output:
Márcio Aguiar | Belmiro Braga Street | 1234-5678 | M | Planets of Solar System
Joana Tobias | Santos Dummont Avenue | 8765-4321 | F | Black Holes, Satellites
Data Consultation
Then there is a consultation system. It has a loop that runs until the file ends. Inside this loop there is a variable named $buffer that gets one line of the txt file each time. It is then exploded to create a array named $lines[$counter]. To print it nicely, I use a array_combine where I join the names of the fields on another array ($keys) to the values written in $lines[$counter], and attibutes that to $combined[$counter]. Then the loop ends and I use a print_r inside <pre></pre> to see the data written in $combined, while mantaining the spaces and breaks that HTML would otherwise ignore. Here is the code:
$keys = array ("Name", "Address", "Telephone", "Sex", "Visited exhibition");
for ($counter=0;!feof($reader);$counter++){
$buffer = fgets($reader);
$lines[$counter] = explode(" | ", $buffer);
$combined[$counter] = array_combine($keys, $lines[$counter]);
}
echo "<pre>";
print_r($combined);
echo "</pre>";
Example of output:
Array
(
[0] => Array
(
[Name] => Márcio Aguiar
[Address] => Belmiro Braga Street
[Telephone] => 1234-5678
[Sex] => M
[Visited exhibitions] => Planets of Solar System
)
[1] => Array
(
[Name] => Joana Tobias
[Address] => Santos Dummont Avenue
[Telephone] => 8765-4321
[Sex] => F
[Visited exhibitions] => Black Holes, Satellites
)
[2] =>
)
Here you can see that a 2 Array was created blank. It's caused by the last line, that contains only a line break inserted by the form above. I need to remove this last line break, and only that one, but don't know how. I want to know! Not knowing causes the exhibition of an error when the execution arrive at the array_combine, because it's needed that the two arrays have the same number of elements, and 2 is blank. Here the error:
Warning: array_combine(): Both parameters should have an equal number of elements in E:\Aluno\Documents\Wamp\www\trab_1\area_restrita\consulta.php on line 60
Original answer:
To remove a trailing line break from any text, you can use trim(), however in your case, you just need to use fopen in append mode:
$handle = fopen("/path/to/file", "a");
Then get rid of the PHP_EOL:
fwrite ($pointer, "$name | $addres | $telephone | $sex | $exhibitions");
Edit: You're right that appending doesn't append to a new line. I was mistaken. So you could use trim() like I mentioned earlier. I created a quick example using file_get_contents() and file_put_contents() and it appears to do what you want:
<?php
$file = 'test.txt';
// Set existing contents (for testing sake)
$orig_contents = "bob | 123 fake street | 1234567890 | yes, please | no\n";
$orig_contents .= "bob | 123 fake street | 1234567890 | yes, please | no\n";
$orig_contents .= "bob | 123 fake street | 1234567890 | yes, please | no\n";
$orig_contents .= "bob | 123 fake street | 1234567890 | yes, please | no\n";
file_put_contents($file, $orig_contents);
// Here is how you could add a single new line with append mode
// Notice the trailing \n
file_put_contents($file, "billy | 456 fake street | 2345678901 | no | yes\n", FILE_APPEND);
// Get contents from the file and remove any trailing line breaks
$contents = trim(file_get_contents($file));
$keys = array ("Name", "Address", "Telephone", "Sex", "Visited exhibition");
// Explode based on the new line character
$lines = explode("\n", $contents);
foreach ($lines as $line) {
$values = explode(" | ", $line);
$combined[] = array_combine($keys, $values);
}
print_r($combined);
This prints:
Array
(
[0] => Array
(
[Name] => bob
[Address] => 123 fake street
[Telephone] => 1234567890
[Sex] => yes, please
[Visited exhibition] => no
)
[1] => Array
(
[Name] => bob
[Address] => 123 fake street
[Telephone] => 1234567890
[Sex] => yes, please
[Visited exhibition] => no
)
[2] => Array
(
[Name] => bob
[Address] => 123 fake street
[Telephone] => 1234567890
[Sex] => yes, please
[Visited exhibition] => no
)
[3] => Array
(
[Name] => bob
[Address] => 123 fake street
[Telephone] => 1234567890
[Sex] => yes, please
[Visited exhibition] => no
)
[4] => Array
(
[Name] => billy
[Address] => 456 fake street
[Telephone] => 2345678901
[Sex] => no
[Visited exhibition] => yes
)
)
The problem is with the way you're reading the file. You're testing for EOF before reading from the file. But feof() won't be true until you try to read while you're at the end of the file.
Instead, you should test whether fgets() returns a line.
for ($counter = 0; $buffer = fgets($reader); $counter++) {
$lines[$counter] = explode(" | ", $buffer);
$combined[$counter] = array_combine($keys, $lines[$counter]);
}
DEMO
To explain further, suppose you have a file with one line in it. When $counter is 0, you call feof(), and it returns false. So you then read the first line, and add it to $lines and $combined. Then you increment $counter and go back to the beginning of the loop.
When $counter is 1, you call feof(). It's still not true, because you haven't tried to read at the end of the file yet. Then you try to read the next line, but there is no line there, fgets returns false and you assign this to $buffer. This is treated as an empty string by explode(), so you add an empty array to $lines and $combined. Then you increment $counter and go back to the beginning of the loop.
Then you call feof(), and this time it returns true because you tried to read at the end of the file on the previous iteration. So the loop ends.
As you can see from the above, even though the file only has 1 line, you end up with 2 entries in your arrays, because you didn't test for EOF until after you read too far.
In case you want to get rid of the last line break in data you pulled from the db ($res) you can use the following snippet
for ($i=0; $i <count($res); $i++) {
// If this is not last item then add items separated by line break
if($i+1 < count($res)) {
file_put_contents("file.txt", $res[$i].PHP_EOL, FILE_APPEND);
}
// If this is the last item, then don't append the final line break
else {
file_put_contents("file.txt", $res[$i], FILE_APPEND);
}
}
For example I have the text
a1aabca2aa3adefa4a
I want to extract 2 and 3 with a regex between abc and def, so 1 and 4 should be not included in the result.
I tried this
if(preg_match_all('#abc(?:a(\d)a)+def#is', file_get_contents('test.txt'), $m, PREG_SET_ORDER))
print_r($m);
I get this
> Array
(
[0] => Array
(
[0] => abca1aa2adef
[1] => 3
)
)
But I want this
Array
(
[0] => Array
(
[0] => abca1aa2adef
[1] => 2
[2] => 3
)
)
Is this possible with one preg_match_all call? How can I do it?
Thanks
preg_match_all(
'/\d # match a digit
(?=.*def) # only if followed by <anything> + def
(?!.*abc) # and not followed by <anything> + abc
/x',
$subject, $result, PREG_PATTERN_ORDER);
$result = $result[0];
works on your example. It assumes that there is exactly one instance of abc and def per line in your string.
The reason why your attempt didn't work is that your capturing group (\d) that matches the digit is within another, repeated group (?:a(\d)a)+. With every repetition, the result of the capture is overwritten. This is how regular expressions work.
In other words - see what's happening during the match:
Current position Current part of regex Capturing group 1
--------------------------------------------------------------
a1a no match, advancing... undefined
abc abc undefined
a2a (?:a(\d)a) 2
a3a (?:a(\d)a) (repeated) 3 (overwrites 2)
def def 3
You ask if it is possible with a single preg_match_all.
Indeed it is.
This code outputs exactly what you want.
<?php
$subject='a1aabca2aa3adefa4a';
$pattern='/abc(?:a(\d)a+(\d)a)def/m';
preg_match_all($pattern, $subject, $all_matches,PREG_OFFSET_CAPTURE | PREG_PATTERN_ORDER);
$res[0]=$all_matches[0][0][0];
$res[1]=$all_matches[1][0][0];
$res[2]=$all_matches[2][0][0];
var_dump($res);
?>
Here is the output:
array
0 => string 'abca2aa3adef' (length=12)
1 => string '2' (length=1)
2 => string '3' (length=1)
I have data which I wish to be pasted into a textbox, it will be in the form E.G
Ryu Aiter D78:21:87:13 177 /177 1 / 6
Ryu Chronos D78:21:26:21 182 /182 0 / 6
Ryu Hermes D78:21:26:22 201 /201 0 / 6
Ryu Hefaistos D78:31:75:10 136 /136 1 / 2
Ryu Krotos D78:84:96:11 170 /170 1 / 6
Ryu Heros D78:65:51:31 175 /175 2 / 5
Ryu Arachnos D78:13:84:11 185 /185 0 / 5
its splits up like this
Base(max 16 chars)
Location(staring D , 12 chars)
econ/max econ (int/int)
used/Total(int/int)
What i wish to do is create a loop for each Row of text,
and then inside that loop chop out each part of the row into variables for each component.
as far as ideas on separating it i know that the : symbol is banned from names and bases.
so if i find the first ":" then step back 2 and take the next 12 chars that is my location
i know that can be done with a until loop and if(string[x]=':')
But how do i loops through rows?
And how can i separate the rest of the data in a row?
This is what regular expressions are for :P try this out:
$lines = explode( "\r\n", $data );
$users = array();
foreach( $lines as $line )
{
$matches = array();
$user = array();
preg_match( "/([^ ]+) ([^ ]+) ((?:[A-Z])(?:[0-9]+:){3}[0-9]+) ([0-9]+) \/([0-9]+) ([0-9]+) \/ ([0-9]+)/", $line, $matches );
list(,$user['name'],$user['base'],$user['location'],$user['econ'],$user['maxecon'],$user['used'],$user['total']) = $matches;
$users[] = $user;
}
You will have an array called users which contains a series of associative arrays with the components. Like below...
Array
(
[0] => Array
(
[total] => 6
[used] => 1
[maxecon] => 177
[econ] => 177
[location] => D78:21:87:13
[base] => Aiter
[name] => Ryu
)
[1] => Array
(
[total] => 6
[used] => 0
[maxecon] => 182
[econ] => 182
[location] => D78:21:26:21
[base] => Chronos
[name] => Ryu
)
etc, etc...
EDIT: I made a lot of assumptions about the data as you haven't given many details, if you need further help with the expression let me know.
UPDATE AS PER YOUR COMMENT:
Line 182: $name = htmlspecialchars(mysql_real_escape_string($_POST['name']));
Line 188: $tecon = htmlspecialchars(mysql_real_escape_string($user['econ']));
You should turn on display_errors as they were simple syntax errors that could easily be debugged.
Can you just use explode and gradually break it down?
eg.
explode the entry into seperate lines at '\n'
then explode each line into 2, everything before the 1st ':', and everything after
explode the 1st part into pieces using each 'space' to give you Name, Base, Location
explode the 2nd part using 'space', ':' or '/' to give you econ/max econ and used/Total
if(preg_match_all('/^([a-z]{1,16})\s+([a-z]{1,16})\s+(D\d+):(\d+):(\d+):(\d+)\s+(\d+)\s+\/(\d+)\s+(\d+)\s+\/\s+(\d+)$/mi', $yourinputgoeshere, $match) {
print_r($match);
}
This should, untested, get everything into a large array, separated. Untested
Extending the same idea i am trying to use preg match for a single line on another page.
i use the code
$data = $_POST['list'];
$matches = array();
$user = array();
preg_match( "/(.+?) ((?:[A-Z])(?:[0-9]+:){3}[0-9]+) ([0-9]+) \/([0-9]+) ([0-9]+) \/ ([0-9]+)/", $data, $matches );
list(,$user['base'],$user['location'],$user['econ'],$user['maxecon'],$user['used'],$user['total']) = $matches;
$base = $users['base'];
$location = $users['location'];
$tecon = $users['econ'];
i used echo to print out £data and it contains the data as expected but the same code without the lines loop does not seperate the parts of my data into the array..in fact the array size of $user remains empty, what has gone wroung?