regex for numbers on multiple lines php - php

I have a file that looks like this (yes the line breaks are right):
39 9
30 30 30 31 34 30 30 32 33 32 36 30 31 38 0D 0A 00014002326018..
39 30 30 30 31 34 30 30 32 33 32 36 30 35 34 0D 900014002326054.
0A .
39 30 30 30 31 34 30 30 32 33 32 36 30 39 31 0D 900014002326091.
0A .
39 30 30 30 31 34 30 30 32 33 32 36 31 36 33 0D 900014002326163.
0A .
39 9
30 30 30 31 34 30 30 32 33 000140023
32 36 32 30 30 0D 0A 26200..
39 9
30 30 30 31 34 30 30 32 33 32 36 32 30 30 0D 0A 00014002326200..
39 30 30 30 31 34 30 30 32 33 32 36 31 32 32 0D 900014002326122.
0A .
39 9
30 30 30 31 34 30 30 32 33 000140023
32 36 31 35 34 0D 0A 26154..
39 30 30 30 31 34 30 30 32 33 9000140023
32 36 31 33 31 0D 0A 26131..
39 9
30 30 30 31 34 30 30 32 33 000140023
32 36 31 30 34 0D 0A 26104..
39 30 30 30 31 34 30 30 32 33 32 36 30 39 30 0D 900014002326090.
0A .
39 30 30 30 31 34 30 30 32 33 32 36 31 39 37 0D 900014002326197.
0A .
39 9
30 30 30 31 34 30 30 32 33 32 36 32 30 38 0D 0A 00014002326208..
39 30 30 30 31 34 30 30 32 33 9000140023
32 36 31 31 35 0D 0A 26115..
39 9
30 30 30 31 34 30 30 32 33 000140023
32 36 31 36 34 0D 0A 26164..
39 9
30 30 30 31 34 30 30 32 33 000140023
32 36 30 31 36 0D 0A 39 30 30 30 31 34 30 30 32 26016..900014002
33 3
32 36 32 34 36 0D 0A 26246..
39 9
30 30 30 31 34 30 30 32 33 000140023
32 36 32 34 36 0D 0A 26246..
39 9
30 30 30 31 34 30 30 32 33 000140023
32 36 30 37 39 0D 0A 26079..
39 9
30 30 30 31 34 30 30 32 33 000140023
32 36 31 32 30 0D 0A 26120..
39 9
30 30 30 31 34 30 30 32 33 32 36 32 32 38 0D 0A 00014002326228..
39 30 30 30 31 34 30 30 32 33 9000140023
32 36 31 38 36 0D 0A 26186..
I have this code that grabs the EID tags (the numbers that start with 9000) but I can't figure out how to get it to do multiple lines.
$data = file_get_contents('tags.txt');
$pattern = "/(\d{15})/i";
preg_match_all($pattern, $data, $tags);
$count = 0;
foreach ( $tags[0] as $tag ){
echo $tag . '<br />';
$count++;
}
echo "<br />" . $count . " total head scanned";
For example the first and second line should return 900014002326018 instead of ignoring the first and second line
I am not good with regular expressions, so if you could explain so I learn and stop having to have someone help me with simple regex, that would be awesome.
EDIT: The whole number is 15 digits starting with 9000

You can do this:
$result = preg_replace('~\R?(?:[0-9A-F]{2}\h+)+~', '', $data);
$result = explode('..', rtrim($result, '.'));
pattern details:
\R? # optional newline character
(?: # open a non-capturing group
[0-9A-F]{2} # two hexadecimal characters
\h+ # horizontal white characters (spaces or tabs)
)+ # repeat the non-capturing group one or more times
After this replacement the only content you must remove are the two dots. After removing the trailing dots, you can use these to explode the string to an array.
An other way
Since you know that there is always 48 characters before the part of integers (and dots), you can use this pattern too:
$result = preg_replace('~(?:^|\R).{48}~', '', $data);
An other way without regex
The idea is to read the file line by line and, since the length before the content is always the same (i.e. 16*3 characters -> 48 characters), extract the substring with the integer and concatenate it into the $data temporary variable.
ini_set("auto_detect_line_endings", true);
$data = '';
$handle = #fopen("tags.txt", "r");
if ($handle) {
while (($buffer = fgets($handle, 128)) !== false) {
$data .= substr($buffer, 48, -1);
}
if (!feof($handle)) {
echo "Error: fgets() has failed\n";
}
fclose($handle);
} else {
echo "Error opening the file\n";
}
$result = explode ('..', rtrim($data, '.'));
Note: if the file has a windows format (with the end of line \r\n) you must change the third parameter of the substr() function to -2. If you are interested by how to detect newlines type, you can take a look at this post.

I don't think it's even possible to do this with a single regex, but your code will be far more legible and maintainable if you approach this one step at a time.
This works, and it shouldn't be too hard to figure out how it works:
$eid_tag_src = <<<END_EID_TAGS
39 9
30 30 30 31 34 30 30 32 33 32 36 30 31 38 0D 0A 00014002326018..
39 30 30 30 31 34 30 30 32 33 32 36 30 35 34 0D 900014002326054.
:
etc.
:
39 30 30 30 31 34 30 30 32 33 9000140023
32 36 31 38 36 0D 0A 26186..
END_EID_TAGS;
/* Remove hex data from first 48 characters of each line */
$eid_tag_src = preg_replace('/^.{48}/m','',$eid_tag_src);
/* Remove all white space */
$eid_tag_src = preg_replace('/\s+/','',$eid_tag_src);
/* Replace dots (CRLF) with spaces */
$eid_tag_src = str_replace('..',' ',$eid_tag_src);
/* Convert to array of EID tags */
$eid_tags = explode(' ',trim($eid_tag_src));
print_r($eid_tags);
Here's the output:
Array
(
[0] => 900014002326018
[1] => 900014002326054
[2] => 900014002326091
[3] => 900014002326163
[4] => 900014002326200
[5] => 900014002326200
[6] => 900014002326122
[7] => 900014002326154
[8] => 900014002326131
[9] => 900014002326104
[10] => 900014002326090
[11] => 900014002326197
[12] => 900014002326208
[13] => 900014002326115
[14] => 900014002326164
[15] => 900014002326016
[16] => 900014002326246
[17] => 900014002326246
[18] => 900014002326079
[19] => 900014002326120
[20] => 900014002326228
[21] => 900014002326186
)

Here's an approach using effective grabbing (without replacing):
RegEx: /(?:^.{48}|\.)([0-9]+\.?)/m - explained demo
Which means (in plain english): start grabbing digits followed by an optional dot IF from the start of the line there are 48 characters in front of them OR a dot (special case).
And your code could look like this:
$pattern = '/(?:^.{48}|\.)([0-9]+\.?)/m';
preg_match_all($pattern, $data, $tags);
//join all the bits belonging to the number
$data=implode("", $tags[1]);
//count the dots to have a correct count of the numbers grabbed
//since each number was grabbed with an ending dot initially
$count=substr_count($data, ".");
//replace the dots with a html <br> tag (avoiding a split and a foreach loop)
$tags=str_replace('.', "<br>", $data);
print $tags . "<br>" . $count . " total scanned";
See the code live at http://3v4l.org/Z4EhI

Related

php tcp socket listener data parse

I have gps tracker which is sending data to my server over tcp and my server has been coded in PHP. the device is sending data in every 5 seconds. The issue is that the first response comes fine as soon as socket established connection with the client but then subsequent responses comes in weird characters and no idea of how to interpret them.
Server.php code
SocketServer.class.php
<?php
require_once("SocketServer.class.php");
$server = new SocketServer("172.xx.xx.xxx",8000);
$server->max_clients = 10;
$server->hook("CONNECT","handle_connect"); // Run handle_connect every time someone connects
$server->hook("INPUT","handle_input");
$server->infinite_loop();
function handle_connect(&$server,&$client,$input) {
SocketServer::socket_write_smart($client->socket,"String? ","");
}
function handle_input(&$server,&$client,$input) {
$trim = trim($input);
if(strtolower($trim) == "quit")
{
SocketServer::socket_write_smart($client->socket,"Oh... Goodbye...");
$server->disconnect($client->server_clients_index); // Disconnect this client.
return;
}
$output = strrev($trim);
SocketServer::socket_write_smart($client->socket,$output);
SocketServer::socket_write_smart($client->socket,"String? ","");
}
Output:
Triggering Hook 'handle_connect' for 'CONNECT'
*HQ,9176952312,V6,115939,V,3127.2665,N,07307.6974,E,0.00,126.00,100822,FFF7FBFF,410,1,325,14688,8992300001243970980F,#
Triggering Hook 'handle_input' for 'INPUT'
0#119.160.59.240 --> $?v?#uY"1'&e0v?
&????ʧ?E9`,
Triggering Hook 'handle_input' for 'INPUT'
0#119.160.59.240 --> $?v?#uY"1'&e0v?
&????ʧ?E9`,
hex dump if data comes correctly
<Buffer 2a 48 51 2c 39 31 37 36 39 35 32 33 37 35 2c 56 36 2c 31 35 35 37 32 32 2c 56 2c 33 31 32 37 2e 33 30 33 34 2c 4e 2c 37 33 30 37 2e 36 36 33 36 2c 45 ... 65 more bytes>
Hex dump incorrect response
24 91 76 95 23 75 15 57 24 10 08 22 31 27 30 34 04 07 30 76 63 0c 00 00 00 ff f7 fb ff 00 01 c7 f0 00 00 00 00 01 9a 01 01 45 7f 2d 00

AWS S3 Bucket Presigned URL

I am trying to create Presigned URLs for users to access content from an S3 bucket.
The below code was working fine and all of a sudden I am getting the below error when opening any pre-signed URL that is created.
public function getPresignedUri($p)
{
$s3 = new S3Client([
'region' => getenv('S3_REGION'),
'version' => 'latest',
]);
$cmd = $s3->getCommand('GetObject', [
'Bucket' => getenv('S3_BUCKET'),
'Key' => 'casts/'. $p['file']
]);
$request = $s3->createPresignedRequest($cmd, '+1 hour');
return (string) $request->getUri();
}
<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><AWSAccessKeyId>ASIA3DM6Y5GJC4FYJAFC</AWSAccessKeyId><StringToSign>AWS4-HMAC-SHA256
20180821T072223Z
20180821/ap-southeast-2/s3/aws4_request
fc4f1139d3b146ae027bd0bfc0b3d6dacda81d711b062e0d93a65d04a61aa268</StringToSign><SignatureProvided>5f5d3ae9ef3d9cdfc0d039c39302c584dcfc93f5a94a0f1770bf6781d6958198</SignatureProvided><StringToSignBytes>41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 31 38 30 38 32 31 54 30 37 32 32 32 33 5a 0a 32 30 31 38 30 38 32 31 2f 61 70 2d 73 6f 75 74 68 65 61 73 74 2d 32 2f 73 33 2f 61 77 73 34 5f 72 65 71 75 65 73 74 0a 66 63 34 66 31 31 33 39 64 33 62 31 34 36 61 65 30 32 37 62 64 30 62 66 63 30 62 33 64 36 64 61 63 64 61 38 31 64 37 31 31 62 30 36 32 65 30 64 39 33 61 36 35 64 30 34 61 36 31 61 61 32 36 38</StringToSignBytes><CanonicalRequest>GET
/casts/5B735D22BCB17.mp4
X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIA3DM6Y5GJC4FYJAFC%2F20180821%2Fap-southeast-2%2Fs3%2Faws4_request&X-Amz-Date=20180821T072223Z&X-Amz-Expires=3600&X-Amz-Security-Token=FQoGZXIvYXdzEPn%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaDEMaw7h8OwK6f6QN0SLBA4%2B9LzXs7OMNjW7HDqr1jhuK%2FshbOMDBoF00GHqTWUuJWXQuL4ptYpvWRjwpris0USWPMTx0O3WeKacvtw6oN2M1KRoUe3IcNOpFwaixKw8%2Fo5FKXK%2BSCo%2F7U%2B76V4aEFuuWEZkC5qhm9R7ChB7vDNTlmYXx2GOzL2uZYV8dZrAnrUfU5qWpyI4IQb8DvPnpDWB0OgA2SRvuGzkwkVLEtMmHS2SMU32gwX2Oy6YnMswWZeqVQ%2FovfWbxd5AA4O%2BFNQfcNM5l4jsuR2zV8FiKZ3jQRLgfQx5uvydv6FFzb90SDbvUjZd0aAsR1Mre%2FnoQodezAm0xoA5618%2FWd%2BIh3jouN2RflRM3II8UXCWzFFq2NL%2FxweJu2mYXfKNpTkqOEls5dFMo2OWQa3IGXJqT3EZEZKXcQ3z%2F2aOP%2Fyw%2F2GtPdQrdJziwN4lTXyl6%2FGZYd968yjlU6pIk6vB0NVq9q3wKjBiwlsfGTlaJnFJH7DD%2FIY4U6fYOmvAcGnoozAbIcqDZpDPNrvZX75tzSatHHLyQoF56STZPhWK7cCWEo2JWAzg6NE4xBmypFG%2Bkxtv0QtrcUNYD35FvFGbjheUhMnyOKOTz7tsF&X-Amz-SignedHeaders=host
host:app-assets-dev-ap-southeast-2-cmpny.s3.ap-southeast-2.amazonaws.com
host
UNSIGNED-PAYLOAD</CanonicalRequest>
AWS SDK verison aws/aws-sdk-php (3.65.0)
One small difference I can see in the URL is that when it was working it had
X-Amz-SignedHeaders=host
and now it has
X-Amz-SignedHeaders=host%3Bx-amz-security-token
Although not sure what could be causing that extra string??
EDIT 1:
I identified that this issue was due to the SDK version 3.65 ... when I rolled back to 3.31 there was no issue.
However I am not marking this as resolved as I would like to know why a small version change like this made such a big difference and error?
I can see there is major differences in the src/Signature/SignatureS4.php file specifically:
$parsed['query']['X-Amz-SignedHeaders'] = 'host'; (V3.31)
and
$parsed['query']['X-Amz-SignedHeaders'] = implode(';', $this->getPresignHeaders($parsed['headers'])); (V3.65)
however that line alone didn't fix - replacing the whole file did fix the error.
I logged this issue on Github - https://github.com/aws/aws-sdk-php/issues/1609
And it was tested, confirmed and resolved very quickly - https://github.com/aws/aws-sdk-php/pull/1610
This is resolved.

How to show these numbers in tabular format?

This code is giving the following output, I want to show these numbers in table is there any way to do this? and why it is not showing the numbers in proper format like why 53 is not exactly below 40 and others also not showing in proper order?
<?php
$arr = array();
for ($i=1;$i<82;$i++) {
$arr[] = $i;
}
shuffle($arr);
$lines = array_chunk($arr, 9);
foreach ($lines as $key => $line) {
$lines[$key] = implode("&nbsp&nbsp&nbsp", $line);
}
echo implode("<br>", $lines);
?>
Output
73 40 79 1 43 7 76 44 18
6 53 45 55 71 20 80 66 74
69 51 52 65 22 63 59 50 54
29 33 23 49 77 24 61 60 58
8 81 30 15 26 32 16 47 31
17 39 4 35 27 11 5 25 68
2 34 72 42 75 46 48 3 38
14 28 37 62 10 78 12 56 13
41 21 19 36 9 64 67 57 70
If you don't want to use tables or CSS. You can use a <pre> tag then use some tabs and newlines
<pre>
<?php
$arr = array();
for ($i=1;$i<82;$i++) {
$arr[] = $i;
}
shuffle($arr);
$lines = array_chunk($arr, 9);
foreach ($lines as $key => $line) {
$lines[$key] = implode("\t", $line);
}
echo implode("\n", $lines);
?>
Fiddle
Note: I didn't really care about your logic for creating those lines while answering this question since its only about formatting. You can trim down some code too.
Output
22 16 66 79 8 41 47 2 80
29 38 76 18 40 46 73 34 45
31 3 62 68 14 33 20 72 67
78 44 42 30 51 77 36 25 48
64 70 21 15 19 9 56 50 65
37 27 4 1 35 74 75 52 32
81 23 10 28 26 59 7 54 11
6 63 5 39 53 12 24 60 49
71 55 17 13 61 69 43 57 58
Now with slightly better code
<pre>
<?php
$numbers=range(1,81);
shuffle($numbers);
$c=0;
foreach($numbers as $n)
{
if($c%9==0)echo "\n";
echo $n."\t";
$c++;
}
?>
modify your code like this:
<?php
$arr = array();
for ($i=1;$i<82;$i++) {
$arr[] = $i;
}
shuffle($arr);
$lines = array_chunk($arr, 9);
echo '<table>';
foreach ($lines as $key => $line) {
echo '<tr><td align="right">';
echo $lines[$key] = implode('</td><td align="right">', $line);
echo '</td></tr>';
}
echo '</table>';
?>
Output
57 41 48 17 73 76 7 78 12
69 61 39 80 24 58 45 11 70
47 65 33 21 38 4 19 13 46
59 52 63 14 25 3 30 28 77
50 40 68 6 2 29 20 66 26
72 74 34 75 15 36 71 10 60
55 53 1 16 23 42 51 35 62
44 32 43 64 18 8 54 49 5
81 27 31 67 37 22 79 56 9
try this
<?php
$arr = array();
for ($i = 1; $i < 82; $i++) {
$arr[] = $i;
}
shuffle($arr);
$lines = array_chunk($arr, 9);
echo "<table>";
foreach ($lines as $key => $line) {
echo "<tr>";
for ($i = 0; $i < sizeof($line); $i++) {
echo "<td align='right'>" . $line[$i] . "</td>";
}
echo "</tr>";
}
echo "</table>";
?>
Your Out put

Was ending character reached or not

In summary I am using stream_get_line to read a line of a file, replace a string and then write the line to another file.
I am using stream_get_line and supplying the "ending" parameter to instruct the function to read lines, or if there is no new line then read 130 bytes.
What I would like to know is how can I know if the 3rd parameter (PHP_EOL) was found, as I need to write exactly the same line (except for my string replacement) to the new file.
For reference...
string stream_get_line ( resource $handle , int $length [, string $ending ] )
It's mainly needed for the last line, sometimes it will contain a newline character and sometimes it doesn't.
My initial idea is to seek to the last line of the file and search the line for a new line character to see if I need to attach a newline to my edited line or not.
You could try using fgets if the stream is in ASCII mode (which only matters on Windows). That function will include the newline if it is found:
$line = fgets(STDIN, 131);
Otherwise, you could use ftell to see how many bytes were read and thus determine whether there was a line ending. For example, if foo.php contains
<?php
while (!feof(STDIN)) {
$pos = ftell(STDIN);
$line = stream_get_line(STDIN, 74, "\n");
$ended = (bool)(ftell(STDIN) - strlen($line) - $pos);
echo ($ended ? "YES " : "NO ") . $line . "\n";
}
executing echo -ne {1..100} '\n2nd to last line\nlast line' | php foo.php will give this output:
NO 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
NO 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 5
NO 3 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
YES 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
YES 2nd to last line
NO last line

windows 7, php 5.3, when fetching binary data with sqlsrv from mssql the data is doubled

ok, so I have images on a MSSQL 2005 (express) server which I want to write to files.
with the same piece of code, on linux it works fine, on windows it writes the data twice in the file
file_put_contents($file, $val);
$val = basename($file);
I know it is not a file_put_contents() problem because I also tried with fwrite
the output file in windows has double the size it has in linux
-rw-rw-r-- 1 dimitris dimitris 891768 2011-11-22 16:13 eshop_products__2201.jpg
-rw-rw-r-- 1 dimitris dimitris 445884 2011-11-21 19:15 eshop_products__2201_linux.jpg
I am using the freetds driver for linux and php_pdo_sqlsrv_53_nts_vc9 in windows
any ideas on something I could do to get the correct data in windows? maybe some configuration I missed?
topmost bytes of each file:
windows file:
ASCII (php substr):
FFD8FFE000104A46494600010100000100010000FFDB0043000302020302020303030304030304050805050404050A070706080C0...etc...
hex:
00000000: 46 46 44 38 46 46 45 30 30 30 31 30 34 41 34 36 FFD8FFE000104A46
00000010: 34 39 34 36 30 30 30 31 30 31 30 30 30 30 30 31 4946000101000001
00000020: 30 30 30 31 30 30 30 30 46 46 44 42 30 30 34 33 00010000FFDB0043
00000030: 30 30 30 33 30 32 30 32 30 33 30 32 30 32 30 33 0003020203020203
00000040: 30 33 30 33 30 33 30 34 30 33 30 33 30 34 30 35 0303030403030405
00000050: 30 38 30 35 30 35 30 34 30 34 30 35 30 41 30 37 0805050404050A07
00000060: 30 37 30 36 30 38 30 43 30 41 30 43 30 43 30 42 0706080C0A0C0C0B
linux file:
ASCII (php substr):
����JFIF��C
hex:
00000000: ff d8 ff e0 00 10 4a 46 49 46 00 01 01 00 00 01 ......JFIF......
00000010: 00 01 00 00 ff db 00 43 00 03 02 02 03 02 02 03 .......C........
00000020: 03 03 03 04 03 03 04 05 08 05 05 04 04 05 0a 07 ................
00000030: 07 06 08 0c 0a 0c 0c 0b 0a 0b 0b 0d 0e 12 10 0d ................
00000040: 0e 11 0e 0b 0b 10 16 10 11 13 14 15 15 15 0c 0f ................
00000050: 17 18 16 14 18 12 14 15 14 ff db 00 43 01 03 04 ............C...
00000060: 04 05 04 05 09 05 05 09 14 0d 0b 0d 14 14 14 14 ................
00000070: 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 ................
The code extracting the file from the database is base on the yii framework, using yii's commands feature (php cli)
Connection strings:
linux
'connectionString' => 'dblib:host=192.168.56.101;port=1433;dbname=mssqldb',
windows
'connectionString' => 'sqlsrv:Server=192.168.56.101;Database=mssqldb',
Code:
$rows = $this->db->createCommand("SELECT * FROM $viewName WHERE 1=1")->queryAll();
/*
* Convert charset and extract photos from view
*/
foreach ($rows as $row) {
// convert charset / export photos
foreach ($row as $key => &$val) {
// charset conversion, on fields that do not contain photos
// $viewCfg is an array I maintain to know which field does what
if (empty($viewCfg['photos']) || !in_array($key, $viewCfg['photos'])) {
$val = #iconv("Windows-1253", "UTF-8", $val);
// grab image in file
} else {
$id = '';
foreach ($viewCfg['keys'] as $fieldName) {
$id .= '_' . $row[$fieldName];
}
$file = Yii::app()->params['pathPhotos'] . '/' . $viewName . '_' . $id . '.jpg';
if ($val) {
if (file_exists($file) && (file_get_contents($file)!=$val))
unlink($file);
file_put_contents($file, $val);
$val = basename($file);
}
}
}
// ... do the rest
Good to hear you found some solution, if not a nice one : /
I had that situation while testing Mssql on a PHP app but never got any further. Another hint you may check is the PHP doc on PDO Large Objects (like varbinary and image SQL Datatypes): http://de2.php.net/manual/en/pdo.lobs.php
Maybe the result is consistent when you read the image as a stream?

Categories