preg_match_all() - 无法在foreach循环中获取匹配的字符串

时间:2012-09-25 09:48:35

标签: php regex string

我遇到与this post中回答的问题类似的问题。

当我测试在该帖子中作为答案提供的正则表达式时,它按预期工作:

  $str = 'Days - £9.20 to £11.20 Sat - £11.80 Sun - £13.30';            

  preg_match_all("/£\s*\d+(?:\.\d+)?/", $str, $matches);                    

  print_r($matches);

  // Produces
  Array
  (
     [0] => Array
        (
          [0] => £9.20
          [1] => £10.20
          [2] => £11.80
         )
   )

当我尝试使用它来处理我在foreach循环中转换为数组的CSV中的数据时出现问题:

foreach($arrJobs as $job)
{
    $str = $job['payDetails1'] . ' ' . $job['payDetails2'];                     

    // Try to find salary from string           

    preg_match_all("/£\s*\d+(?:\.\d+)?/", $str, $matches);

    print_r($matches);                  
}

// In this example the output from every item is an empty array:

 Array
 (
   [0] => Array
       (
       )

  )

我在第一个例子中用来测试函数的字符串是通过在第二个例子中回显$ str的值并复制并粘贴它来获得的。

我不明白为什么相同的字符串会返回不同的结果?为什么当我将字符串粘贴到变量中时它工作正常但是当从CSV中检索字符串时没有找到匹配项?

1 个答案:

答案 0 :(得分:1)

[答案来自上述评论和反馈]

问题

此处的问题是您的源文件和CSV输入未使用相同的字符编码保存。

PHP中的所有内置字符串函数(包括未使用/u标志时的PCRE函数)在字节序列上盲目操作,不能识别字符。这意味着对于包含ASCII范围之外的字符的脚本,运行时行为将根据保存脚本的编码而改变,因为这些字符将针对实际使用的每个编码以不同方式转换为字节。您的脚本包含一个这样的字符:pound sign

快速解决方案

假设此处可能的编码为ISO 8859-1 (Western European)UTF-8,则正则表达式匹配的所有剩余字符在两种编码中具有相同的表示形式,因此它们不会出现任何问题。那么让我们看看我们可以对英镑符号做些什么。

通常,您可以通过将文字£替换为涵盖所有字符编码的替换组来解决此问题。

        POUND SIGN ( £ )
when encoded in    is represented as
------------------------------------
ISO 8859-1         0xA3
UTF-8              0xC2 0xA3

那将是(\xa3|\xc2\xa3) - 第一部分涵盖ISO 8859-1和第二部分UTF-8。但是,看到两个部分都以\xa3结尾,\xc2?\xa3也可以得到相同的结果(使\xc2前缀可选)。

因此,您可以通过将代码更改为

以一种快速和肮脏的方式解决您的问题
preg_match_all('/\xc2?\xa3\s*\d+(?:\.\d+)?/', $str, $matches);

更好的解决方案

然而,最好的解决方案是始终使用UTF-8。要做到这一点,你需要

  1. 将脚本保存为UTF-8
  2. 确保输入CSV为UTF-8开头,或者在处理之前将其转换为UTF-8(您可以使用iconv执行此操作)
  3. 通过这种方式,您可以返回在脚本中保存文字符号,并且无论CSV数据的输入编码是什么,它都可以安全地知道它将正常工作。