文字空白字符导致模式失败(有时)

时间:2017-12-06 05:35:53

标签: php regex file whitespace preg-match-all

我在上一个问题中有这个RegEx。问题在于它有时会起作用,有时它并不适用。我尝试在在线模拟器上粘贴它并获得:https://regex101.com/r/I3tnY4/3

该文字来自我使用

阅读的文件
  

的file_get_contents

文件的内容已经完成,但是当我通过RegEx运行它来过滤它时:

        $data = file_get_contents($var);
        $pat  = '/intervals \[\d+\]:\s+\Kxmin = (?P<xmin>\d+(\.\d+)?) \
                \s+xmax = (?P<xmax>\d+(\.\d+)?)\s+text = "(?P<text>[^"]*)"/m';

        // print_r($data);
        preg_match_all($pat, $data, $m);
        $result = array_map(function($a){
            return array_combine(['xmin', 'xmax', 'text'], $a);
        }, array_map(null, $m['xmin'], $m['xmax'], $m['text']));

        print_r($result);

它返回一个空数组。起初,它正在工作,但当我添加一个for循环来处理多个文件上传时,它停止了工作。

这也发生在我上传文件后立即处理文件之前。

像这样:

if (move_uploaded_file($_FILES["uploadedfile"]["tmp_name"], $target_file)) {
        if (file_exists($target_file)) {   
            $data = file_get_contents($target_file);
            $pat  = '/intervals \[\d+\]:\s+\Kxmin = (?P<xmin>\d+(\.\d+)?) \
            \s+xmax = (?P<xmax>\d+(\.\d+)?)\s+text = "(?P<text>[^"]*)"/m';


            preg_match_all($pat, $data, $m);
            $result = array_map(function($a){
               return array_combine(['xmin', 'xmax', 'text'], $a);
            }, array_map(null, $m['xmin'], $m['xmax'], $m['text']));

            print_r($result);
        }
    }

使用上面的代码,由于$ result数组为空,RegEx也失败了。我想这是因为文件尚未准备好被阅读或其他东西。即使我打印文件的内容,一切都在那里。所以我所做的就是将我的页面重定向到另一个执行RegEx处理的文件,并且令人惊讶的是它在那里工作。

2 个答案:

答案 0 :(得分:2)

您的任务似乎更侧重于子字符串提取,而不是验证。因此,您可以使用以下模式大大减小模式的大小,加快执行速度并最大限度地减少输出膨胀:

/xmin = (\S+)\s+xmax = (\S+)\s+text = "([^"]*)/

我做了什么? (有关官方模式细分,请参阅this demo

  • 删除排名interval...匹配,因为您没有使用它(或更具体地说是[]:
  • 中的数字
  • 删除\K,因为您不需要重启&#34;全字符串匹配 - 你没有使用它。
  • 删除指定的捕获组,因为您使用array_map()array_combine()无论如何都要分配这些键名。命名捕获组会导致主要输出数组膨胀,除非您有令人信服的理由使用它们,否则应该避免使用它们。它们导致膨胀的原因是因为当您命名捕获组时,preg_match_all()将写入重复的子数组元素(命名的元素和索引的元素) - 这意味着必要的数据加倍。虽然,是的,您可以使用命名捕获组,这只是意味着您将更改mapping进程以从每个子数组中删除所有索引元素([0],[1],[2],[3])。
  • 删除模式中的中断。如果要容纳一个或多个空白字符(在您的情况下:换行符,空格和可能的选项卡),只需使用\s+。为了记录,您可以在您的模式中使用空格来提高可读性,但要做到这一点,您需要在模式的末尾包含x作为标记。 x模式修饰符将忽略模式中使用的所有空格,因此请注意此效果。
  • (?P<xmax>\d+(\.\d+)?)替换为(\S+)。这将删除命名的捕获组和嵌套的捕获组,并提取整个非空白子字符串。如果您想验证此字符串,那么我建议:(\d+(?:\.\d+)?)这会将嵌套组更改为&#34;非捕获&#34; - 再次减少输出阵列膨胀。
  • 在最后一个捕获组中使用否定捕获组是明智的,这是匹配它的最有效方法。您不需要尾随",因此可以删除。
  • 删除m模式修饰符。您没有使用任何锚元字符(^$),因此该标志没有用处。
  • preg_match_all()的第四个参数PREG_SET_ORDER将以这样的方式构建您的子数组:设置多维数组只需要一个array_map()

我建议你实施它:

代码:(Demo

$data='intervals [1]:
    xmin = 0 
    xmax = 13.139997023062838 
    text = "" 
intervals [2]:
    xmin = 13.139997023062838 
    xmax = 14.763036269953904 
    text = "Cities are like siblings in a large polygamous family." 
intervals [3]:
    xmin = 14.763036269953904 
    xmax = 17.01 
    text = ""';
$pat='/xmin = (\S+)\s+xmax = (\S+)\s+text = "([^"]*)/';
if(preg_match_all($pat,$data,$m,PREG_SET_ORDER)){
    $assoc_multidim=array_map(function($a){return array_combine(['xmin','xmax','text'],array_slice($a,1));},$m);
    var_export($assoc_multidim);
}else{
    echo "substring extraction failed";
}

输出:

array (
  0 => 
  array (
    'xmin' => '0',
    'xmax' => '13.139997023062838',
    'text' => '',
  ),
  1 => 
  array (
    'xmin' => '13.139997023062838',
    'xmax' => '14.763036269953904',
    'text' => 'Cities are like siblings in a large polygamous family.',
  ),
  2 => 
  array (
    'xmin' => '14.763036269953904',
    'xmax' => '17.01',
    'text' => '',
  ),
)

使用命名捕获组的另一种方法如下所示:(Demo

$pat='/xmin = (?P<xmin>\S+)\s+xmax = (?P<xmax>\S+)\s+text = "(?P<text>[^"]*)/';
if(preg_match_all($pat,$data,$m,PREG_SET_ORDER)){
    $assoc_multidim=array_map(function($a){return array_intersect_key($a,['xmin'=>'','xmax'=>'','text'=>'']);},$m);
    var_export($assoc_multidim);
}else{
    echo "substring extraction failed";
}

...你看,这两种技术都需要一点点清理(除非你的流程要注意不要注意索引的子数组),这就是为什么我喜欢不那么臃肿的数组。

答案 1 :(得分:1)

请尝试使用以下正则表达式(DEMOPHP Demo):

 /(intervals \[\d+\]:)\s+\Kxmin = (?P<xmin>\d+(\.\d+)?)\s+xmax = (?P<xmax>\d+(\.\d+)?)\s+text = "(?P<text>[^"]*)"/m

没有必要把所有额外的空间都放在\s+上就可以了。