Reading content from positional text file in PHP? - 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;
}

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: Finding a set of numbers in a database that sums up to a particular number

Firstly, i am a php newbie... so i still code and understand php procedurally. that said,
i have a collection of numbers (amount) stored in a database.
Question: Using PHP and mySQL,
Whats the best way to spool this info out of a database such that the amount will be associated with its transaction ID
Most importantly, i need to find a matching set of numbers in the db that equals a sum of 29.
Below is the Transaction Table , Transaction_tlb, for my Database mydb
Transaction_ID | Name | Date | Amount
---------------|------------------|-----------------|------------
11012 | Jonathan May | 6/12/2016 | 84
21012 | John Pedesta | 6/12/2016 | 38
31012 | Mary Johnson | 1/01/2017 | 12
41012 | John Johnson | 8/01/2017 | 13
51012 | Keith Jayron | 8/01/2017 | 17
61012 | Brenda Goldson | 8/01/2017 | 2
71012 | Joshua Traveen | 8/01/2017 | 78
81012 | Remy ma Goldstein| 8/01/2017 | 1
91012 | Barbie Traveen | 8/01/2017 | 1
Now, i have an idea..but its not efficient. I am going to try every possible case. meaning if i have n values to check, the time complexity is going to be about 2^n. this is highly inefficient (plus, i dont even know if my code makes any sense. (see below)
I saw a similar example in this YouTube video: https://www.youtube.com/watch?v=XKu_SEDAykw&t
but, Im not sure exactly how to write the code in php.
The code:
<?php
if (!mysql_connect("localhost", "mysql_user", "mysql_password") || !mysql_select_db("mydb")) {
die("Could not connect: " . mysql_error()); } //End DB Connect
$capacity = 29; //Knapsack Capacity or Sum
//Select Transact ID and Value from the Database where Amount is <= Capacity
$fetchQuery = "SELECT 'Transaction_ID', 'Amount' FROM 'Transaction_tlb' WHERE 'Amount' <= $capacity";
$components = array(); //new array to hold components
if ($queryResults = mysql_query($fetchQuery)) {
//check if data was pulled
if (mysql_num_row($queryResults) != NULL) {
while ($row = mysqli_fetch_assoc($queryResults) {
$components[$row['Transaction_ID']] = $row['Amount'];
}
}
}
/* Correct me if i am wrong, but, Components associative array Should be something like
$components = array('11012'=> 84, '21012'=> 38, '31012'=> 12, '41012'=> 13, '51012'=> 17,
'61012'=> 2, '71012'=> 78, '81012'=> 1, '91012'=> 1);
*/
$components = asort($components) // sort array in ascending order
$componentCount = count($component)
function match ($componentCount, $capacity) {
$temp = match (($componentCount - 1), $capacity);
$temp1 = $component[$componentCount] + match (($componentCount - 1), ($capacity - $component[$componentCount]));
$result = max($temp, $temp1);
return $result;
}
}?>
can anyone please point me in the right direction? this code doesn work... and even if it works... the method is not efficient at all. what happens when Ive got 3 million records to work with? i need help please.
You can formulate your problem in terms of the 0/1 Knapsack problem. Ready-to-use implementation in PHP is available.
Using the function knapSolveFast2 defined in the linked page, one could proceed as in the example below. The idea here is that you set the "weights" entering the Knapsack algorithm equal to the values themselves.
$components = array(84, 38, 12, 13, 17, 2, 78, 1, 1);
$m = array();
list($m4, $pickedItems) = knapSolveFast2($components, $components, sizeof($components)-1, 29, $m);
echo "sum: $m4\n";
echo "selected components:\n";
foreach($pickedItems as $idx){
echo "\t$idx --> $components[$idx]\n";
}
which yields:
sum: 29
selected components:
2 --> 12
4 --> 17
Notes:
you could modify your SQL query in order to skip rows with amount larger than the required sum (29)
the function above will pick one solution (assuming that it exists), it won't provide all of them
one should check whether the return value $m4 is indeed equal to the specified sum (29) - as the algorithm works, the specified amount is only the upper limit which is not guaranteed to be attained (for example for 37 instead of 29, the return value is only 34 since there is no combination of the input numbers the sum of which would yield 37)
This is really a knapsack problem, but I will try to give a full solution which isn't optimal, but illustrates a full strategy for solving your problem.
First of all, you can do this with just one iteration over the array of numbers with no recursion and no pre-sorting needed. Dynamic programming is all you need, keeping track of all previously possible partial-sum 'paths'. The idea is somewhat similar to your described recursive method, but we can do it iteratively and without presorting.
Assuming an input array of [84, 38, 12, 13, 17, 2, 78, 1, 1] and a target of 29, we loop over the numbers like so:
* 84 - too big, move on
* 38 - too big, move on
* 12 - gives us a subtarget of 29-12 = 17
subtargets:
17 (paths: 12)
* 13 - gives us a subtarget of 29-13=16
subtargets:
16 (paths: 13)
17 (paths: 12)
* 17 - is a subtarget, fulfilling the '12' path;
and gives us a subtarget of 29-17=12
subtargets:
12 (paths: 17)
16 (paths: 13)
17 (paths: 12)
solutions:
12+17
etc.
The trick here is that while looping over the numbers, we keep a lookup table of subTargets, which are the numbers which would give us a solution using one or more combinations ('paths') of previously seen numbers. If a new number is a subTarget, we add to our list of solutions; if not then we append to existing paths where num<subTarget and move on.
A quick and dirty PHP function to do this:
// Note: only positive non-zero integer values are supported
// Also, we may return duplicate addend sets where the only difference is the order
function findAddends($components, $target)
{
// A structure to hold our partial result paths
// The integer key is the sub-target and the value is an array of string representations
// of the 'paths' to get to that sub-target. E.g. for target=29
// subTargets = {
// 26: { '=3':true },
// 15: { '=12+2':true, '=13+1':true }
// }
// We are (mis)using associative arrays as HashSets
$subTargets = array();
// And our found solutions, stored as string keys to avoid duplicates (again using associative array as a HashSet)
$solutions = array();
// One loop to Rule Them All
echo 'Looping over the array of values...' . PHP_EOL;
foreach ($components as $num) {
echo 'Processing number ' . $num . '...' . PHP_EOL;
if ($num > $target) {
echo $num . ' is too large, so we skip it' . PHP_EOL;
continue;
}
if ($num == $target) {
echo $num . ' is an exact match. Adding to solutions..' . PHP_EOL;
$solutions['='.$num] = true;
continue;
}
// For every subtarget that is larger than $num we get a new 'sub-subtarget' as well
foreach ($subTargets as $subTarget => $paths) {
if ($num > $subTarget) { continue; }
if ($num == $subTarget) {
echo 'Solution(s) found for ' . $num . ' with previous sub-target. Adding to solutions..' . PHP_EOL;
foreach ($paths as $path => $bool) {
$solutions[$path . '+' . $num] = true;
}
continue;
}
// Our new 'sub-sub-target' is:
$subRemainder = $subTarget-$num;
// Add the new sub-sub-target including the 'path' of addends to get there
if ( ! isset($subTargets[$subRemainder])) { $subTargets[$subRemainder] = array(); }
// For each path to the original sub-target, we add the $num which creates a new path to the subRemainder
foreach ($paths as $path => $bool) {
$subTargets[$subRemainder][$path.'+'.$num] = true;
}
}
// Subtracting the number from our original target gives us a new sub-target
$remainder = $target - $num;
// Add the new sub-target including the 'path' of addends to get there
if ( ! isset($subTargets[$remainder])) { $subTargets[$remainder] = array(); }
$subTargets[$remainder]['='.$num] = true;
}
return $solutions;
}
Run the code like so:
$componentArr = array(84, 38, 12, 13, 17, 2, 78, 1, 1);
$addends = findAddends($componentArr, 29);
echo 'Result:'.PHP_EOL;
foreach ($addends as $addendSet => $bool) {
echo $addendSet . PHP_EOL;
}
which outputs:
Looping over the array of values...
Processing number 84...
84 is too large, so we skip it
Processing number 38...
38 is too large, so we skip it
Processing number 12...
Processing number 13...
Processing number 17...
Solution(s) found for 17 with previous sub-target. Adding to solutions..
Processing number 2...
Processing number 78...
78 is too large, so we skip it
Processing number 1...
Processing number 1...
Solution(s) found for 1 with previous sub-target. Adding to solutions..
Result:
=12+17
=12+13+2+1+1

Transposing csv values in php

I need to transpose some values in some csv files that we get sent on a regular basis so that they are able to be imported into a website and I'm not sure the best way to go about doing it.
The data arrives in a csv file with a header row containing the column names, and the first column values are product ID's. The data looks like this…
ID F F-VF VF VF-XF XF
1 840 960 1080 1248 1944
2 1137.5 1300 1462.5 1690 2632.5
3 1225 1400 1575 1820 2835
What I'm looking to do is change this around so the column name and it's value are put into a new line for each value for the same id like so…
ID COND VALUE
1 F 840
1 F-VF 960
1 VF 1080
1 VF-XF 1248
1 XF 1944
2 F 1137.5
2 F-VF 1300
2 VF 1462.5
2 VF-XF 1690
2 XF 2835
I may also need to add some strings into the cells - is that easy to do?
Thanks a lot
Not necessarily the most elegant version, just to get an idea.
Something like this would work in case it's a existing csv, which gets read and overwritten with the transposed version.
// assuming a dataset like
// ----
// fruit, apple, pear, peach
// animal, eagle, bear, wolf
// car, renault, fiat, nio
$f = fopen($savePath, 'r');
$header = [];
$data = [];
while($row = fgetcsv($f, 0, ",")) {
$header[]=$row[0];
for ($i = 1; $i < sizeof(array_keys($row)); $i++) {
$data[$i][$row[0]]=$row[$i];
}
}
fclose($f);
$f = fopen($savePath, 'w');
fputcsv($f, $header);
foreach ($data as $recordColDataSet) {
fputcsv($f, array_values($recordColDataSet));
}
fclose($f);
Transposing arrays could also be something to look at eg in this question here:
Transposing multidimensional arrays in PHP
Have you tried any of the standard PHP methods like: str_getcsv() or fgetcsv()?
A quick search of "php csv" provides a TON of possible solutions. I suggest trying a few, then reporting back here if you have a specific problem.

how to import data from text file without any delimiter or separator?

I have to do a task of importing data to mysql from a text file using php code, yeah it's sound so easy, and I have already done that before like importing data from csv file, from excel file or any text file where data is separated with any delimiter. But in my current case, there is no any delimiter just two spaces and the fix length of field. For example-
table field
|------|-----------|-----------|-------------|
| id(8)| name(50) | state(15) | category(10)|
|------|-----------|-----------|-------------|
| | | | |
sample data of upload.txt file-
::format::
ID NAME ADDRESS CATEGORY
10719922 Union Bank of India delhi normal
10719956 State Bank of India mumbai normal
10719522 HDFC Bank gujrat high
10759924 ICICI Bank goa normal
Now you can understand the data format of text file, i.e. field length + two spaces, field length + two spaces and so on. The problem is if the data is not matches with the field size then again spaces are hare to complete the length of field that's why two space are not available as delimiter. Like take the first of data- id have 8 digit data than two spaces and name length 50 but data have only 19 character so there is 31 spaces to complete the length 50 after that two space then next field. So I have no delimiter or syntax (rather than length + 2 spaces) to identify the single field data. I am very confused how to import this data to MySQL using php script. does any one think It can be happen. Please I need some idea or php code, to handle this situation. Thank You
It shouldn't be more difficult than this:
<?php
$input = <<<END
10719922 Union Bank of India delhi normal
10719956 State Bank of India mumbai normal
10719522 HDFC Bank gujrat high
10759924 ICICI Bank goa normal
END;
$def = array(
"id" => 8,
"name" => 50,
"state" => 15,
"category" => 10
);
foreach (explode(PHP_EOL, $input) as $line) {
foreach ($def as $field => $length) {
$value = substr($line, 0, $length + 2);
$line = substr($line, $length + 2);
print $field.' = '.trim($value).PHP_EOL;
}
print '----------------------------------------'.PHP_EOL;
}
?>
Basic idea is to create a format definition in the $def hash, and then process all lines according to that format definition.
Executing this code will yield the output below. Change the actual implementation to fit your needs.
id = 10719922
name = Union Bank of India
state = delhi
category = normal
----------------------------------------
id = 10719956
name = State Bank of India
state = mumbai
category = normal
----------------------------------------
id = 10719522
name = HDFC Bank
state = gujrat
category = high
----------------------------------------
id = 10759924
name = ICICI Bank
state = goa
category = normal
----------------------------------------
You could use preg_split() function, and explode the string/line/row by >= 2 spaces:
$line = '10719922 Union Bank of India delhi normal';
$m = preg_split('~(\h{2,})~', $line);
print_r($m);
demo

Converting a string of data to an array with PHP

I have to convert a long string of data into values so that I can import them into my database. Unfortunately, the data is displayed as text and not XML, so I need a way to convert this into, ideally, a key->value array.
The data looks like this:
AU - Author 1
AU - Author 2
AU - Author 3
LA - ENG
PT - ARTICLE
DEP - 235234
TA - TA
JN - Journal name
JID - 3456346
EDAT- 2011-11-03 06:00
MHDA- 2011-11-03 06:00
CRDT- 2011-11-03 06:00
TI - multi-line text text text text text
text text tex tex text
text text tex tex text
After researching, it seems like explode could be a viable means to accomplish this, but I'm not sure how to implement it in this scenerio, or if there is a better method of accomplishing this. Especially since there can be random hyphens and line breaks in the middle of the string.
Any help much appreciated in advance!
Since values can contain dashes and be spread across multiple lines, I think the safest method for separating keys from values is using substr(), since the separating dashes always sit at the same character position in the string.
FIXED
<?php
// first, split into lines
$lines = explode("\n",str_replace(array("\r\n","\r"),"\n",$data));
// this will hold the parsed data
$result = array();
// This will track the current key for multi-line values
$thisKey = '';
// Loop the split data
foreach ($lines as $line) {
if (substr($line,4,1) == '-') {
// There is a separator, start a new key
$thisKey = trim(substr($line,0,4));
if ($result[$thisKey]) {
// This is a duplicate value
if (is_array($result[$thisKey])) {
// already an array
$result[$thisKey][] = trim(substr($line,5));
} else {
// convert to array
$result[$thisKey] = array($result[$thisKey],trim(substr($line,5)));
}
} else {
// Not a duplicate value
$result[$thisKey] = trim(substr($line,5));
}
} else {
// There is no separator, append data to the last key
if (is_array($result[$thisKey])) {
$result[$thisKey][count($result[$thisKey]) - 1] .= PHP_EOL.trim(substr($line,5));
} else {
$result[$thisKey] .= PHP_EOL.trim(substr($line,5));
}
}
}
print_r($result);
?>
See it working

Categories