使用 PCRE 正则表达式从类似 CSV 的字符串中提取值,包括空字段
Extract values from CSV-like string including empty fields with PCRE regex
我尝试从多个类似 csv 的简单数据列字符串中捕获包括(!)空列的列值,用分号分隔。即使我知道正则表达式不是最好的方法并且显式 csv 解析器会做得更好,在这种情况下我别无选择,只能使用 PRCE 正则表达式来构建 html table <td>
个来自该数据的组。
应该(仍然)有效的更糟糕的示例如下所示:
;testvalue;"testvalue";"test "val"ue";test value;
... 字面上应该这样解释:
empty | testvalue | testvalue | test "val"ue | test value | empty
...最终呈现为这个(不是问题的一部分):
<td>empty</td>
<td>testvalue</td>
<td>testvalue</td>
<td>test"val"ue</td>
<td>test value</td>
<td>empty</td>
(按照@anubhava 的要求进行更新)
可悲的是,还有另一个 downer 随之而来:将实施它的系统有一个固定的方式来处理字符串。它只会识别并且只会改变捕获的字符串组。字符串的任何其他未注册部分将直接与其余部分 原样 一起打印出来。这意味着:我们需要注册正则表达式中的分号,即使我们不希望它们被打印出来,而是通过忽略它们的匹配组来删除它们。
通常只打印捕获的组就可以了,但是这里不行。仅捕获值将导致此输出:
;;;;;
<td>empty</td>
<td>testvalue</td>
<td>testvalue</td>
<td>test"val"ue</td>
<td>test value</td>
<td>empty</td>
也许我们需要在另一组中先捕获整个字符串,或者我们需要在另一组中捕获分号以便稍后在打印输出时将其丢弃? ...
尝试以下 PCRE 正则表达式:
\"(?:.*?)\"(?=;|$)|(?<=(?:;))(?:.*?)(?=;|$)|^(?:[^;]*?)(?=;)
从示例字符串 ;testvalue;"testvalue";"test value";test value;
正则表达式将捕获:
Match 1:
Match 2: testvalue
Match 3: "testvalue"
Match 4: "test value"
Match 5: test value
Match 6:
查看演示
您可以使用这个更简单的正则表达式,在第三个捕获组中使用包含分号的后视:
$str = ';testvalue;"testvalue";"test "val"ue";test value;';
preg_match_all('/(?<=;|^)("?)([^;]*)(;|$)/', $str, $matches);
print_r($matches[2]);
(?<=;|^)
是一个积极的回顾,以确保我们仅在行开始或 ;
.
之后才匹配 [^;]*
输出:
Array
(
[0] =>
[1] => testvalue
[2] => testvalue
[3] => test "val"ue
[4] => test value
[5] =>
)
并获得所需的 HTML:
echo "<td>" . implode("</td>\n<td>", $matches[2]) . "</td>\n";
<td></td>
<td>testvalue</td>
<td>testvalue</td>
<td>test "val"ue</td>
<td>test value</td>
<td></td>
我尝试从多个类似 csv 的简单数据列字符串中捕获包括(!)空列的列值,用分号分隔。即使我知道正则表达式不是最好的方法并且显式 csv 解析器会做得更好,在这种情况下我别无选择,只能使用 PRCE 正则表达式来构建 html table <td>
个来自该数据的组。
应该(仍然)有效的更糟糕的示例如下所示:
;testvalue;"testvalue";"test "val"ue";test value;
... 字面上应该这样解释:
empty | testvalue | testvalue | test "val"ue | test value | empty
...最终呈现为这个(不是问题的一部分):
<td>empty</td>
<td>testvalue</td>
<td>testvalue</td>
<td>test"val"ue</td>
<td>test value</td>
<td>empty</td>
(按照@anubhava 的要求进行更新)
可悲的是,还有另一个 downer 随之而来:将实施它的系统有一个固定的方式来处理字符串。它只会识别并且只会改变捕获的字符串组。字符串的任何其他未注册部分将直接与其余部分 原样 一起打印出来。这意味着:我们需要注册正则表达式中的分号,即使我们不希望它们被打印出来,而是通过忽略它们的匹配组来删除它们。
通常只打印捕获的组就可以了,但是这里不行。仅捕获值将导致此输出:
;;;;;
<td>empty</td>
<td>testvalue</td>
<td>testvalue</td>
<td>test"val"ue</td>
<td>test value</td>
<td>empty</td>
也许我们需要在另一组中先捕获整个字符串,或者我们需要在另一组中捕获分号以便稍后在打印输出时将其丢弃? ...
尝试以下 PCRE 正则表达式:
\"(?:.*?)\"(?=;|$)|(?<=(?:;))(?:.*?)(?=;|$)|^(?:[^;]*?)(?=;)
从示例字符串 ;testvalue;"testvalue";"test value";test value;
正则表达式将捕获:
Match 1:
Match 2: testvalue
Match 3: "testvalue"
Match 4: "test value"
Match 5: test value
Match 6:
查看演示
您可以使用这个更简单的正则表达式,在第三个捕获组中使用包含分号的后视:
$str = ';testvalue;"testvalue";"test "val"ue";test value;';
preg_match_all('/(?<=;|^)("?)([^;]*)(;|$)/', $str, $matches);
print_r($matches[2]);
(?<=;|^)
是一个积极的回顾,以确保我们仅在行开始或 ;
.
[^;]*
输出:
Array
(
[0] =>
[1] => testvalue
[2] => testvalue
[3] => test "val"ue
[4] => test value
[5] =>
)
并获得所需的 HTML:
echo "<td>" . implode("</td>\n<td>", $matches[2]) . "</td>\n";
<td></td>
<td>testvalue</td>
<td>testvalue</td>
<td>test "val"ue</td>
<td>test value</td>
<td></td>