3

I'm working on a website and want admins to be able upload a .txt file with employee ID's and names. I need the .txt file to be parsed and insert information into the database, but I keep getting errors while parsing.

I tried following the format of PHP - parsing a txt file but after my first $row explode() I start getting several illegal or undefined offset errors.

The .txt:

186298  "Cushing,Kathy N"
167876  "Roberts,Joseph Allen"
109876  "Smith,Sarah Quinn"
118679  "Hernandez,Juan"
187568  "Freeman,Colby Matthew"

The .php function:

function updateMemberList()
{
        global $db_handle;
        $tmpName = $_SESSION['filename'];
        //$include_once ($_SERVER['DOCUMENT_ROOT'] . "../../uploads/updates/'$tmpName'");
        $txt_file = file_get_contents("../../uploads/updates/$tmpName");
        $rows = explode("\n", $txt_file);

        foreach($rows as $row => $data)
        {
            //Splits data into employee ID and full name
            $row_data = explode(' ', $data);

            $info[$row]['empid'] = $row_data[0];
            $info[$row]['name'] = $row_data[1];     //AFTER this line is where I start having errors

            //Splits name into last name and first name
            $row_name = explode(',', $info[$row]['name']);
            $info[$row_name]['lname'] = $row_name[0];
            $info[$row_name]['fname'] = $row_name[1];

            //Cleans " off last name
            $row_lname = explode('"', $info[$row_name]['lname']);
            $info[$row_lname]['lname'] = $row_lname[1];

            //Cleans middle name and " off first name
            $row_fname1 = explode(' ', $info[$row]['fname']);
            $info[$row]['fname'] = $row_fname1[0];
            $row_fname2 = explode('"', $info[$row]['fname']);
            $info[$row]['fname'] = $row_fname2[0];

            //Declares variables
            $uname = $info[row]['fname'] + "." + $info[$row]['lname'];
            $fname = $info[row]['fname'];
            $lname = $info[$row]['lname'];
            $empid = $info[$row]['empid'];

            //Checks to see if user is already in db
            $query = ("SELECT * FROM user WHERE username = '$uname'");
            $check = mysqli_query($db_handle, $query);
            $num_rows = $check->num_rows;

            //If user isn't in db, generates a password and adds them
            if ($num_rows < 1)
            {
                //Generates random 8 character password
                $length = 8;
                $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
                $charactersLength = strlen($characters);
                for ($i = 0; $i < $length; $i++)
                {
        $randomString[$i] = $characters[rand(0, $charactersLength - 1)];
                }
                $temppass = implode($randomString);
                $pword = password_hash($temppass, PASSWORD_DEFAULT);
                $addto = "INSERT INTO `user`(`username`, `first_name`, `last_name`, `employee_id`, `password_hash`, `isActive`, `firstLogin`) VALUES ('$uname', '$fname', '$lname', '$empid', '$pword', 1, 1)";
                mysqli_query($db_handle, $addto);
            }
        }
    }

Is there any way to parse after the tab that would be more simple to grab the first and last name and doesn't yell at me?

EDIT: Solved issues by swapping to a .csv instead of .txt

Cœur
  • 37,241
  • 25
  • 195
  • 267
Lance
  • 43
  • 9
  • 1
    Does it have to be plain text? Best would be a properly structured data format like JSON or XML but otherwise even using CSV would make things more explicit. This is destined to give you headaches as you cant control the quality of the input. By using JSON for example you can at least be sure you are being passed a parse-able file before you start trying to parse it (ie by doing `json_decode(file_get_contents($file))`) – Mike Miller Jul 16 '15 at 13:48
  • It comes in a .xlsx so it can be converted into a .csv – Lance Jul 16 '15 at 13:51
  • use file() with explode() in foreach – Robert Jul 16 '15 at 13:51
  • 1
    Check out the native CSV functions they will remove a lot of effort from you. Developing out to catch all edge cases will be time consuming http://php.net/manual/en/function.str-getcsv.php – Mike Miller Jul 16 '15 at 13:54
  • I'll check that out Mike, thank you. – Lance Jul 16 '15 at 13:56
  • `explode()` is rarely suitable for such tasks. And that doesn't seem CSV, more like TSV. Use a regex for more consistent extraction. Also can avoid line-wise iteration. (And I wouldn't call either "parsing".) – mario Jul 16 '15 at 13:56

4 Answers4

3

Use regex it makes it much simpler also you don't need explode('\n') just file() function

  foreach($rows as $row => $data) { 
     $matches = array();
     preg_match('#([0-9]+).*? "([^""]+?)"#', $data, $matches);
     echo $matches[1]; //id
     echo $matches[2]; //name
  }

also you can change

    $txt_file = file_get_contents("../../uploads/updates/$tmpName");
    $rows = explode("\n", $txt_file);

to

    $rows = file("../../uploads/updates/$tmpName");

Morover, to generate random 8 length string use

$temppass = bin2hex(openssl_random_pseudo_bytes(4));

it's much more secure.

Robert
  • 19,800
  • 5
  • 55
  • 85
  • What is $t in the foreach() supposed to be? – Lance Jul 16 '15 at 14:10
  • 1
    in your case it should be `$data` I've edited answer – Robert Jul 16 '15 at 14:12
  • Getting undefined offset errors on the echo $matches[] lines – Lance Jul 16 '15 at 14:14
  • 1
    use exactly the code I've given you there is no change you get this error I've checked this code in fiddle. Here's proof -> http://ideone.com/u7xDFF – Robert Jul 16 '15 at 14:19
  • Still throwing the error, I have no idea why it's doing it. I'll keep trying to figure it out, though. Thank you for your help! EDIT: I plugged in your string and it worked, so it's having some issues somewhere trying to pull the data from my .txt file. – Lance Jul 16 '15 at 14:24
  • so you have different data than you posted in question maybe you have empty strings so you should check this in loop. for example `if (empty($data)) { continue; }` What's more you can check if preg_match returns true if so then you won't get undefined index. if it returns false then you should skip this record because data is invalid – Robert Jul 16 '15 at 16:08
0

Try this. First line of while loop:

//Splits data into employee ID and full name
$row_data   = array_map("trim", array_filter(explode('"', $data)));
$names      = explode(",", $row_data[1]);
$result     = array(
    "id"        => $row_data[0],
    "forname"   => $names[1],
    "surname"   => $names[0],
);

And $result should look like this:

Array
(
    [id] => 186298
    [forname] => Kathy N
    [surname] => Cushing
)

Hope this is helpful.

Christian
  • 1,557
  • 11
  • 16
0

Followed advice of https://stackoverflow.com/users/1552594/mike-miller and swapped from .txt to .csv file. This made everything much easier to run through and solved all errors.

Community
  • 1
  • 1
Lance
  • 43
  • 9
-1

You need to explode with 2 spaces:

$row_data = explode('  ', $data);
Daan
  • 12,099
  • 6
  • 34
  • 51
  • Adding another space to that actually caused parsing errors further up the code. – Lance Jul 16 '15 at 13:53
  • I can see `$info[row]['fname'] + "." + $info[$row]['lname']` that this is the wrong way to concatenate strings. – Daan Jul 16 '15 at 13:55
  • 1
    You can `trim($info[$row]['name'], '"')` the double quotes off which is much shorter. – Daan Jul 16 '15 at 13:56
  • The implode works, the password generator is copied straight from another function that is working perfectly. Also, I had no idea there was a trim() function, thank you! – Lance Jul 16 '15 at 13:57