将 PHP mySQL 与包含 BOM 的 CSV 数据一起使用

Using PHP mySQL with CSV data containing BOM

我有一个数据库,用于保存由不同供应商提供的某些商品的库存水平。每个供应商都会每天向我发送一份 CSV 文件,其中包含他们当前的库存水平。我正在尝试将库存水平更新到我的数据库中。

我遇到的问题是,当我从 CSV 中提取数据并通过查询发送它时,它无法正常工作。

我在发送之前回应了查询,输出没有问题。使用 phpMyAdmin,如果我只是在回显时粘贴代码,它就可以正常工作。这让我相信这是一个编码问题。

在 cPanel 文件管理器中查看 CSV 文件,我发现文件开头有一个奇怪的字符。 (我相信这是一个 BOM)。如果我删除这个特征并保存 CSV 文件,那么我的代码可以完美运行并且数据库会按预期更新。

在 cPanel 文件管理器中编辑文件,编码打开为 ansi_x3.110-1983。虽然手动删除角色可以解决问题,但这不是一个选项,因为我希望这是一个完全自动化的日常流程。

我打开文件并从 CSV 中提取数据的代码:

// Open File        
$csvData = fopen($file, "r");
       
if($csvData !== FALSE)
{
  while(!feof($csvData))
  {
      $csvRow[] = fgetcsv($csvData, 100);
  }
}

// Close file
fclose($csvData);

我用于构建简单搜索查询的代码

foreach($csvRow as $row)
{
  $searchQuery = "SELECT * FROM supplier WHERE supplierItemCode = '".$row[0]."'";
  $result = $conn->query($searchQuery);
  echo "<br>".$searchQuery;
  if($result->num_rows > 0)
  {
      // CODE NEVER REACHES HERE
  }

如前所述,如果我只是将 $searchQuery 的回显粘贴到 phpMyAdmin 和 运行 查询中,它就可以正常工作。

我试过使用 fseek($csvData, 2) 成功地从第一行数据中删除 BOM 字符,但是没有效果。

按照提示,我试过使用

$csvData = fopen($file, "r");
$BOM = null;
if($csvData !== FALSE)
{
   $BOM = fread($csvData, 3);
   if($BOM !==  FALSE)
   {
      if($BOM != "\xef\xbb\xbf")
      {
         echo "<h5>BOM: ".$BOM; // This code is executed every time
         fseek($csvData, 0);
      }
   }
   //fseek($csvData, 2); // This was my earlier attempts without the above BOM filter
   while(!feof($csvData))
   {
      $csvRow[] = fgetcsv($csvData, 100);
   }
}

使用 BOM 过滤器方法生成此输出。

作为进一步说明,您会注意到在我的更新查询输出中,SET 数量列中有一个空白 space。此 space 在 csv 文件中不可见。

此查询是用

构建的
$updateQuery = "UPDATE supplier SET ".$supplier." = '".$row[2]."' WHERE supplierItemCode = '".$row[0]."'";

关于导致此问题的确切原因以及我如何解决它的任何建议。

提前致谢。

尝试对打开和读取 CSV 文件的代码进行以下修改。它检查 BOM 是否存在,如果存在则绕过它:

$cvsRow = [];
// Open File
$csvData = fopen($file, "r");
if($csvData !== FALSE)
{
  $BOM = fread($csvData, 4); // read potential BOM sequences to see if one is present or not
  if ($BOM !== FALSE)
  {
    if (strlen($BOM) >= 3 && substr_compare($BOM, "\xef\xbb\xbf", 0, 3) == 0)
    {
      fseek($csvData, 3); // found UTF-8 encoded BOM
    }
    elseif (strlen($BOM) >= 2 && (substr_compare($BOM, "\xfe\xff", 0, 2) == 0 || substr_compare($BOM, "\xff\xfe", 0, 2) == 0))
    {
      fseek($csvData, 2); // found UTF-16 encoded BOM
    }
    elseif ($BOM != "[=10=][=10=]\xfe\xff" && $BOM != "\xff\xfe[=10=][=10=]")
    {
      fseek($csvData, 0); // did not find UTF-32 encoded BOM
    }
    while(!feof($csvData))
    {
        $csvRow[] = fgetcsv($csvData, 100);
    }
  }
  // Close file (only if it has been successfully opened)
  fclose($csvData);
}

我终于找到了可行的解决方案。经过大量调查后,我相信它是用 UTF-16 编码的,尽管 BOM 字符可能一直在说什么。

我刚刚编写了一个简单的函数来将我传递的每个 CSV 值转换为 SQL。

function Convert($str)
    {
        return mb_convert_encoding($str, "UTF-8", "UTF-16BE");
    }

........

$updateQuery = "UPDATE supplier SET ".$supplier." = '".Convert($row[2])."' WHERE supplierItemCode = '".Convert($row[0])."'";
                

我不确定为什么 BOM 会导致此类问题以及为什么完全删除它不起作用。感谢大家的帮助,让我发现了编码问题。