PHP csv import需要帮助 - 插入表时,十进制值变为舍入值

时间:2017-11-23 12:50:52

标签: php csv htmlspecialchars

我正在尝试使用PHP将数据从csv文件插入到MySql表中,

请看这张图片 - 我的CSV文件。 enter image description here

我遇到的问题是,在加载CSV文件时,net_sales列会变为圆角并插入表格中。

插入后请看下面MySql表的示例图像。 enter image description here

仅供参考,我在这里只显示了net_sales列来解释我的问题,实际上我在表格和CSV中有更多列。

由于某些原因,我不能使用像"insert into tran_detail (tran_id,tran_datetime,net_sales) values (...)";

这样的静态插入语句

所以我更喜欢这句话"INSERT INTO tran_detail (".implode(',', array_keys($data)).") VALUES('".implode('\',\'', array_map("convert",array_values($data)))."')";

这是我用于插入的PHP。请帮助在表中插入带小数的值,就像在CSV中一样。

    function convert($string)
    {
        return htmlspecialchars($string,ENT_QUOTES);
    }

    $columnArray    = array();
    $dataArray      = array();
    $firstRule      = true;

    while ($data = fgetcsv ($source, 1000, ","))
    {
        if($firstRule)
        {
            foreach($data as $columnName)
            {
                $columnArray[] = $columnName;
            }

            $firstRule = false;
        }
        else
        {
            $rule = array();
            for($i = 0; $i < count($data) ; $i++)
            {
                $rule[$columnArray[$i]] = $data[$i];
            }
            $dataArray[] = $rule;
        }
    }

    foreach($dataArray as $data)
    {

    $query = "INSERT INTO `tran_detail` (".implode('`,`', array_keys($data))."`) VALUES('".implode('\',\'', array_map("convert",array_values($data)))."')";

        mysql_query($query) or mysql_error();

    }

    fclose($source);

1 个答案:

答案 0 :(得分:3)

这是我这样做的方式:

<?php
/*
   PDO named placeholders require that the array keys are matched
   so we have to prefix them with a colon : as in 'field' becomes ':field'
   the benefit here is the array key order is irrelevant,
   so your csv could have the headers in any order.
*/
function prefixPdoArray(array $array){
    return array_map(function($item){
        return ':'.$item;
    }, $array);
}

//PDO database driver
$dsn = 'mysql:dbname=testdb;host=127.0.0.1';
$user = 'dbuser';
$password = 'dbpass';

$db = new PDO($dsn, $user, $password);

//header array should match DB fields
$header_map = [
    'field1',
    'field2',
    'field3',
    'field4',
];

$placeholders = prefixPdoArray($header_map);

//prepare the query outside of the loop
$stmt = $db->prepare('INSERT INTO `tran_detail` (`'.implode('`,`', $header_map).'`)VALUES('.implode(',', $placeholders).')');

/*
    we can dynamically build the query because $header_map and $placeholders
    are "canned" data, but you could just type it out as well.

    if you do the SQL manually you can dump $header_map and $placeholders
    and manually create $default_map. You could also dump this function
    prefixPdoArray() and just move the array map to $headers.
    so it would be a bit more efficient, but I thought I would show you
    a proper way to build the query dynamically.
*/

$default_map = array_fill_keys($placeholders, '');

//read the first line
$headers = fgetcsv($source, 1000, ","); 
//$header_count = count($csv_headers); //for error chcking if needed

//prefix csv headers
$headers =  prefixPdoArray($headers);

while ($data = fgetcsv($source, 1000, ",")){      
    /*
        array combine will throw an error if the header length
        is different then the data length.
        this indicates a missing or extra delimiter in the csv file.
        you may or may not have to check for this condition  
        -------------------------------------
        if( $header_count != count($data) ){ //do something on error }
    */
    //map file data to file headers
    $csv_mapped = array_combine( $headers, $data);

    //map file row to database query
    $csv_mapped = array_replace($default_map, $csv_mapped );

    //execute the query
    $stmt->execute($csv_mapped);
}

fclose($source);

注意我只能对此进行有限的测试(没有数据库或文件),因此,为了测试和解释,这里是用于测试基本功能的代码。

<?php
function prefixPdoArray(array $array){
    return array_map(function($item){
        return ':'.$item;
    }, $array);
}

//header array should match DB fields
$header_map = [
    'field1',
    'field2',
    'field3',
    'field4',
];

$placeholders = prefixPdoArray($header_map);
echo str_pad(' Placeholders ', 45, '-', STR_PAD_BOTH)."\n";
var_dump($placeholders);

//prepare the query
echo "\n".str_pad(' Raw SQL ', 45, '-', STR_PAD_BOTH)."\n";
echo 'INSERT INTO `tran_detail` (`'.implode('`,`', $header_map).'`)VALUES('.implode(',', $placeholders).')';

$default_map = array_fill_keys($placeholders, '');
echo "\n\n".str_pad(' Default Map ', 45, '-', STR_PAD_BOTH)."\n";
var_dump($default_map);

//(CANNED TEST DATA) read the first line
//example data for testing ( missing field1 ), and field3 out of order
$headers =  [
    'field3',
    'field2',
    'field4',
];
//prefix headers with placeholders
$headers =  prefixPdoArray($headers);

echo "\n".str_pad(' CSV headers ', 45, '-', STR_PAD_BOTH)."\n";
var_dump($headers);

//while ($data = fgetcsv($source, 1000, ",")){
    //(CANNED TEST DATA) read the data line(s)
    //example data for testing ( missing field1 ), and field3 out of order
    $data = [
        'value3',
        'value2',
        'value4',
    ];
    echo "\n".str_pad(' CSV data ', 45, '-', STR_PAD_BOTH)."\n";
    var_dump($data); 

    $csv_mapped = array_combine( $headers, $data);
    echo "\n".str_pad(' CSV mapped data ', 45, '-', STR_PAD_BOTH)."\n";
    var_dump($csv_mapped); 

    $csv_mapped = array_replace($default_map, $csv_mapped );
    echo "\n".str_pad(' CSV filled data ', 45, '-', STR_PAD_BOTH)."\n";
    var_dump($csv_mapped); 
//}

输出

    --------------- Placeholders ----------------
array(4) {
  [0]=>   string(7) ":field1"
  [1]=>   string(7) ":field2"
  [2]=>   string(7) ":field3"
  [3]=>   string(7) ":field4"
}

------------------ Raw SQL ------------------
INSERT INTO `tran_detail` (`field1`,`field2`,`field3`,`field4`)VALUES(:field1,:field2,:field3,:field4)

---------------- Default Map ----------------
array(4) {
  [":field1"]=>   string(0) ""
  [":field2"]=>   string(0) ""
  [":field3"]=>   string(0) ""
  [":field4"]=>   string(0) ""
}

---------------- CSV headers ----------------
array(3) {
  [0]=>   string(7) ":field3"
  [1]=>   string(7) ":field2"
  [2]=>   string(7) ":field4"
}

----------------- CSV data ------------------
array(3) {
  [0]=>   string(6) "value3"
  [1]=>   string(6) "value2"
  [2]=>   string(6) "value4"
}

-------------- CSV mapped data --------------
array(3) {
  [":field3"]=>   string(6) "value3"
  [":field2"]=>   string(6) "value2"
  [":field4"]=>   string(6) "value4"
}

-------------- CSV filled data --------------
array(4) {
  [":field1"]=>   string(0) ""
  [":field2"]=>   string(6) "value2"
  [":field3"]=>   string(6) "value3"
  [":field4"]=>   string(6) "value4"
}

你可以在这里查看。

http://sandbox.onlinephpfunctions.com/code/ab868ac6c6fbf43d74cf62ef2907b0c72e1f59bf

输出中最重要的部分是最后2个数组,因为您可以看到我们如何将数据映射到文件头,然后使用$default_map填写任何缺少的列。你可以在你需要的地方放置任何默认值,例如null或你有什么,但是你必须手动而不是使用$default_map = array_fill_keys($placeholders, '');

如果不随意问的话,这应该是非常自我解释的。

希望我得到它们之间的所有内容以及数据库和文件的内容,如果不是它应该非常接近。但这是相当复杂的代码,所以我可能错过了一些不可思议的事情。

重要的是,它可以让您以优雅的方式映射CSV数据,并避免任何SQL注入错误。