【问题标题】:Using PHP mySQL with CSV data containing BOM将 PHP mySQL 与包含 BOM 的 CSV 数据一起使用
【发布时间】:2020-12-11 13:49:51
【问题描述】:

我有一个数据库,其中包含不同供应商提供的某些商品的库存水平。每个供应商每天都会向我发送一份包含其当前库存水平的 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 数量列中有一个空格。此空间在 csv 文件中不可见。

这个查询是用

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

关于究竟是什么导致此问题以及如何解决此问题的任何建议。

提前致谢。

【问题讨论】:

  • 你是什么意思,“我已经尝试使用 fseek($csvData, 2) 成功地从第一行数据中删除 BOM 字符,但这没有任何效果。”这对我来说似乎是矛盾的,
  • 嗨,布布。我的意思是我在while 循环之前使用了fseek($csvData, 2); 并从CSV 中提取数据。这从 CSV 文件第一行的第一个元素中删除了 BOM 字符,但对 SQL 没有影响。除非我从 CSV 文件中删除 BOM 字符然后重新保存,否则我的任何查询都不起作用。我的代码工作正常。
  • 我的意思是如果它成功删除了 BOM,为什么它没有效果?
  • 我也不懂。因此,我来​​这里寻求帮助。在研究这个问题之前,我从未听说过 BOM 数据。也许它不像我想的那样工作。

标签: php mysql csv byte-order-mark fgetcsv


【解决方案1】:

尝试对打开和读取 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 != "\00\00\xfe\xff" && $BOM != "\xff\xfe\00\00")
    {
      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);
}

【讨论】:

  • 这非常有效。谢谢。虽然我不太清楚为什么。不只是检查前 3 个字符是否是 BOM,如果是,则跳过它们?我已经知道 BOM 在那里,所以 fseek($csvData, 2) 不是在做同样的事情吗?
  • 当且仅当 BOM 存在时,此代码有条件地跳过 BOM,因此它是适用于任何 CSV 文件的通用解决方案。至于为什么您的 fseek 不起作用(我认为只要 BOM is 存在就应该),您实际上并没有使用 fseek 发布任何代码,所以我不能说。如果您愿意,请使用您说您使用过但没有用的fseek 更新您的问题。完成此操作后,请在此处向我发送评论,我会看一下。
  • 我刚刚注意到:你需要做 fseek($csvData, 3);跳过前 3 个字节。
  • 道歉布布。我刚刚意识到这个解决方案不起作用。我第一次尝试的文件是我修改过的文件。我刚刚再次尝试了未触及的 3 个文件,直接在我收到它们时,它无法正常工作。它没有将字符检测为 BOM。我会用截图更新我的问题。
  • 听起来有问题的字符不是真正的 BOM,或者是不同格式的 BOM。显然 BOM 可以是 2、3 或 4 个字节长,并且可以是以下五种格式之一:simple.wikipedia.org/wiki/Byte_order_mark。试试这个脚本来转储前 5 个字符的序数值:$file = 'yourfile.csv'; $fp = fopen($file, 'r'); for ( $i=0; $i &lt; 5; $i++ ) { echo "$i: " . ord($i) . "\n"; } 然后在这里向我们展示结果。
【解决方案2】:

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

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

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 会导致此类问题,以及为什么完全删除它不起作用。感谢大家的帮助,让我发现了编码问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-24
    相关资源
    最近更新 更多