php:将字符串拆分为关联数组的更好方法

php:将字符串拆分为关联数组的更好方法,php,regex,string,split,array-combine,Php,Regex,String,Split,Array Combine,我有这样一个字符串: "ALARM_ID/I4=1010001 ALARM_STATE/U4=eventcode ALARM_TEXT/A=WMR_MAP_EXPORT LOTS/A[1]=[ STEFANO ] ALARM_STATE/U1=1 WAFER/U4=1 VI_KLARF_MAP/A=/test/klarf.map KLARF_STEPID/A=StepID KLARF_DEVICEID/A=DeviceID KLARF_EQUIPMENTID/A=EquipmentID

我有这样一个字符串:

"ALARM_ID/I4=1010001 ALARM_STATE/U4=eventcode ALARM_TEXT/A=WMR_MAP_EXPORT LOTS/A[1]=[ STEFANO ] ALARM_STATE/U1=1 WAFER/U4=1 VI_KLARF_MAP/A=/test/klarf.map KLARF_STEPID/A=StepID KLARF_DEVICEID/A=DeviceID KLARF_EQUIPMENTID/A=EquipmentID KLARF_SETUP_ID/A=SetupID RULE_ID/U4=1234 RULE_FORMULA_EXPRESSION/A=a < b && c > d RULE_FORMULA_TEXT/A=1 < 0 && 2 > 3 RULE_FORMULA_RESULT/A=FAIL TIMESTAMP/A=10-Nov-2020 09:10:11 99999999"
我的目标是分割成一个关联数组:

Array
(
    [ALARM_ID/I4] => 1010001
    [ALARM_STATE/U4] => eventcode
    [ALARM_TEXT/A] => WMR_MAP_EXPORT
    [LOTS/A[1]] => [ STEFANO ]
    [ALARM_STATE/U1] => 1
    [WAFER/U4] => 1
    [VI_KLARF_MAP/A] => /test/klarf.map
    [KLARF_STEPID/A] => StepID
    [KLARF_DEVICEID/A] => DeviceID
    [KLARF_EQUIPMENTID/A] => EquipmentID
    [KLARF_SETUP_ID/A] => SetupID
    [RULE_ID/U4] => 1234
    [RULE_FORMULA_EXPRESSION/A] => a < b && c > d
    [RULE_FORMULA_TEXT/A] => 1 < 0 && 2 > 3
    [RULE_FORMULA_RESULT/A] => FAIL
    [TIMESTAMP/A] => 10-Nov-2020 09:10:11 99999999
)
我发现的独特但可能是肮脏的方式是通过以下脚本:

<?php
$msg = "ALARM_ID/I4=1010001 ALARM_STATE/U4=eventcode ALARM_TEXT/A=WMR_MAP_EXPORT LOTS/A[1]=[ STEFANO ] ALARM_STATE/U1=1 WAFER/U4=1 VI_KLARF_MAP/A=/test/klarf.map KLARF_STEPID/A=StepID KLARF_DEVICEID/A=DeviceID KLARF_EQUIPMENTID/A=EquipmentID KLARF_SETUP_ID/A=SetupID RULE_ID/U4=1234 RULE_FORMULA_EXPRESSION/A=a < b && c > d RULE_FORMULA_TEXT/A=1 < 0 && 2 > 3 RULE_FORMULA_RESULT/A=FAIL TIMESTAMP/A=10-Nov-2020 09:10:11 99999999";
$split = explode("=", $msg);
foreach($split as $k => $s) {
    $s = explode(" ", $s);
    $keys[] = array_pop($s);
    if ($s) $values[] = implode(" ", $s);
}
/*
 * this is needed if last parameter TIMESTAMP does not have ' ' (spaces) into value
 */
if (count($values) + 2 == count($keys)) array_push($values, array_pop($keys));
else                                    $values[ count($values) - 1 ] .= " " . array_pop($keys);
$params = array_combine($keys, $values);
print_r($params);
?>

你有没有发现一种更好的分割方法,也许是使用正则表达式或者其他优雅的方法?方法?

我使用基本的PHP函数管理这段代码。我认为正则表达式使代码更难阅读。大多数情况下,即使以拥有更详细的代码为代价,最好不要使用正则表达式。这也可能会对性能产生影响

$message = "ALARM_ID/I4=1010001 ALARM_STATE/U4=eventcode ALARM_TEXT/A=WMR_MAP_EXPORT LOTS/A[1]=[ STEFANO ] ALARM_STATE/U1=1 WAFER/U4=1 VI_KLARF_MAP/A=/test/klarf.map KLARF_STEPID/A=StepID KLARF_DEVICEID/A=DeviceID KLARF_EQUIPMENTID/A=EquipmentID KLARF_SETUP_ID/A=SetupID RULE_ID/U4=1234 RULE_FORMULA_EXPRESSION/A=a < b && c > d RULE_FORMULA_TEXT/A=1 < 0 && 2 > 3 RULE_FORMULA_RESULT/A=FAIL TIMESTAMP/A=10-Nov-2020 09:10:11 99999999";

foreach (explode(' ', $message) as $word) {
    if (strpos($word, '=')) {
        if (isset($key)) $parameters[$key] = $value; 
        list($key, $value) = explode('=', $word);
    }
    else $value .= " $word";
}    
$parameters[$key] = $value;     

echo '<pre>';
print_r($parameters);
echo '</pre>';
我选择在空格上拆分,然后查找=字符以查找包含键的单词

当然,还有其他方法可以做到这一点,但由于消息的格式奇怪,所有这些都需要额外的工作


此例程目前不允许消息字符串中出现错误,但可以轻松扩展以允许各种类型的输入错误。

我使用基本PHP函数管理此代码。我认为正则表达式使代码更难阅读。大多数情况下,即使以拥有更详细的代码为代价,最好不要使用正则表达式。这也可能会对性能产生影响

$message = "ALARM_ID/I4=1010001 ALARM_STATE/U4=eventcode ALARM_TEXT/A=WMR_MAP_EXPORT LOTS/A[1]=[ STEFANO ] ALARM_STATE/U1=1 WAFER/U4=1 VI_KLARF_MAP/A=/test/klarf.map KLARF_STEPID/A=StepID KLARF_DEVICEID/A=DeviceID KLARF_EQUIPMENTID/A=EquipmentID KLARF_SETUP_ID/A=SetupID RULE_ID/U4=1234 RULE_FORMULA_EXPRESSION/A=a < b && c > d RULE_FORMULA_TEXT/A=1 < 0 && 2 > 3 RULE_FORMULA_RESULT/A=FAIL TIMESTAMP/A=10-Nov-2020 09:10:11 99999999";

foreach (explode(' ', $message) as $word) {
    if (strpos($word, '=')) {
        if (isset($key)) $parameters[$key] = $value; 
        list($key, $value) = explode('=', $word);
    }
    else $value .= " $word";
}    
$parameters[$key] = $value;     

echo '<pre>';
print_r($parameters);
echo '</pre>';
我选择在空格上拆分,然后查找=字符以查找包含键的单词

当然,还有其他方法可以做到这一点,但由于消息的格式奇怪,所有这些都需要额外的工作


此例程目前不允许消息字符串中出现错误,但可以轻松扩展以允许各种类型的输入错误。

您可以利用所有键中存在a/的优势

$re = '`([^\s=/]+/[^\s=]+)=(.*?)(?=\h+[^\s=/]+/|$)`';
$str = 'ALARM_ID/I4=1010001 ALARM_STATE/U4=eventcode ALARM_TEXT/A=WMR_MAP_EXPORT LOTS/A[1]=[ STEFANO ] ALARM_STATE/U1=1 WAFER/U4=1 VI_KLARF_MAP/A=/test/klarf.map KLARF_STEPID/A=StepID KLARF_DEVICEID/A=DeviceID KLARF_EQUIPMENTID/A=EquipmentID KLARF_SETUP_ID/A=SetupID RULE_ID/U4=1234 RULE_FORMULA_EXPRESSION/A=a < b && c > d RULE_FORMULA_TEXT/A=1 < 0 && 2 > 3 RULE_FORMULA_RESULT/A=FAIL TIMESTAMP/A=10-Nov-2020 09:10:11 99999999
';

preg_match_all($re, $str, $matches);
$result = array_combine($matches[1], $matches[2]);

print_r($result);
解释

捕获组1 [^\s=/]+匹配0+乘以除空格=或之外的任何字符/ /[^\s=]+然后匹配/后跟键的其余部分 封闭组1 按字面意思匹配 .*? 捕获组2,尽可能匹配除换行符以外的任何字符 ?=\h+[^\s=/]+/|$断言一种类似键的格式,其中包含组1中使用的/ 见a和a

示例代码


您可以利用a/在所有键中的存在

$re = '`([^\s=/]+/[^\s=]+)=(.*?)(?=\h+[^\s=/]+/|$)`';
$str = 'ALARM_ID/I4=1010001 ALARM_STATE/U4=eventcode ALARM_TEXT/A=WMR_MAP_EXPORT LOTS/A[1]=[ STEFANO ] ALARM_STATE/U1=1 WAFER/U4=1 VI_KLARF_MAP/A=/test/klarf.map KLARF_STEPID/A=StepID KLARF_DEVICEID/A=DeviceID KLARF_EQUIPMENTID/A=EquipmentID KLARF_SETUP_ID/A=SetupID RULE_ID/U4=1234 RULE_FORMULA_EXPRESSION/A=a < b && c > d RULE_FORMULA_TEXT/A=1 < 0 && 2 > 3 RULE_FORMULA_RESULT/A=FAIL TIMESTAMP/A=10-Nov-2020 09:10:11 99999999
';

preg_match_all($re, $str, $matches);
$result = array_combine($matches[1], $matches[2]);

print_r($result);
解释

捕获组1 [^\s=/]+匹配0+乘以除空格=或之外的任何字符/ /[^\s=]+然后匹配/后跟键的其余部分 封闭组1 按字面意思匹配 .*? 捕获组2,尽可能匹配除换行符以外的任何字符 ?=\h+[^\s=/]+/|$断言一种类似键的格式,其中包含组1中使用的/ 见a和a

示例代码


在保持准确性方面要做的重要事情是确保钥匙正确匹配

键字符串永远不会包含空格或等号。值字符串可以包含以下内容之一。值字符串将运行到字符串的末尾,或后跟空格,然后是下一个键,该键可能没有空格或等号

在出现第一个遇到的=之前,可以贪婪地匹配键字符串

值字符串不能完全匹配。这样可以确保值不会过度扩展到下一个键值对中

值字符串后的前瞻确保潜在的跟随键不会被损坏/使用

模式分解:

$msg = "ALARM_ID/I4=1010001 ALARM_STATE/U4=eventcode ALARM_TEXT/A=WMR_MAP_EXPORT LOTS/A[1]=[ STEFANO ] ALARM_STATE/U1=1 WAFER/U4=1 VI_KLARF_MAP/A=/test/klarf.map KLARF_STEPID/A=StepID KLARF_DEVICEID/A=DeviceID KLARF_EQUIPMENTID/A=EquipmentID KLARF_SETUP_ID/A=SetupID RULE_ID/U4=1234 RULE_FORMULA_EXPRESSION/A=a < b && c > d RULE_FORMULA_TEXT/A=1 < 0 && 2 > 3 RULE_FORMULA_RESULT/A=FAIL TIMESTAMP/A=10-Nov-2020 09:10:11 99999999";

preg_match_all('~([^=]+)=(.+?)(?=$| [^ =]+=)~', $msg, $out);
var_export(array_combine($out[1], $out[2]));
代码:

输出:


在保持准确性方面要做的重要事情是确保钥匙正确匹配

键字符串永远不会包含空格或等号。值字符串可以包含以下内容之一。值字符串将运行到字符串的末尾,或后跟空格,然后是下一个键,该键可能没有空格或等号

在出现第一个遇到的=之前,可以贪婪地匹配键字符串

值字符串不能完全匹配。这样可以确保值不会过度扩展到下一个键值对中

值字符串后的前瞻确保潜在的跟随键不会被损坏/使用

模式分解:

$msg = "ALARM_ID/I4=1010001 ALARM_STATE/U4=eventcode ALARM_TEXT/A=WMR_MAP_EXPORT LOTS/A[1]=[ STEFANO ] ALARM_STATE/U1=1 WAFER/U4=1 VI_KLARF_MAP/A=/test/klarf.map KLARF_STEPID/A=StepID KLARF_DEVICEID/A=DeviceID KLARF_EQUIPMENTID/A=EquipmentID KLARF_SETUP_ID/A=SetupID RULE_ID/U4=1234 RULE_FORMULA_EXPRESSION/A=a < b && c > d RULE_FORMULA_TEXT/A=1 < 0 && 2 > 3 RULE_FORMULA_RESULT/A=FAIL TIMESTAMP/A=10-Nov-2020 09:10:11 99999999";

preg_match_all('~([^=]+)=(.+?)(?=$| [^ =]+=)~', $msg, $out);
var_export(array_combine($out[1], $out[2]));
代码:

输出:


你能换一下你要的绳子吗?更好的做法是以JSON或XML等格式获取接收到的字符串,这样更容易避免意外的解析错误。或者你不能影响你接收字符串的方式吗?@DefinitelynotRafal不幸的是,我不能。该字符串是以VFEI虚拟工厂设备接口格式从自动化主机接收的,该格式是不可更改的标准格式。您可以更改获得的字符串吗?更好的做法是以JSON或XML等格式获取接收到的字符串,这样更容易避免意外的解析错误。或者你不能影响你接收字符串的方式吗?@DefinitelynotRafal不幸的是,我不能。该字符串以VFEI虚拟工厂设备接口格式从自动化主机接收,该格式是不可更改的标准。只是出于好奇:为什么有时使用\s,而\h使用其他tim
E我知道\s也包含回车符和一些垂直空格,但由于原始字符串似乎不包含任何空格,我想知道。示例输入中没有提到制表符或换行符。\s、\h和m对我来说似乎都是不必要的。@Jeto公平的问题是,我在否定字符类[^\s=]+中使用了\s来匹配除键的空白字符之外的任何字符,因为\s还可以匹配我认为键中不需要的换行符。我在断言中使用\h来匹配水平空白字符,以确保值位于同一行。我认为对于这个示例数据,您可以同时使用\s或\h两种方式。@m不应该在那里,我是从regex101生成的代码中复制粘贴的。如果只想匹配空格而不是\s或\h,则可以。我用它来匹配范围更广的whitspace字符。只是出于好奇:为什么有时使用\s,有时使用\h?我知道\s也包含回车符和一些垂直空格,但由于原始字符串似乎不包含任何空格,我想知道。示例输入中没有提到制表符或换行符。\s、\h和m对我来说似乎都是不必要的。@Jeto公平的问题是,我在否定字符类[^\s=]+中使用了\s来匹配除键的空白字符之外的任何字符,因为\s还可以匹配我认为键中不需要的换行符。我在断言中使用\h来匹配水平空白字符,以确保值位于同一行。我认为对于这个示例数据,您可以同时使用\s或\h两种方式。@m不应该在那里,我是从regex101生成的代码中复制粘贴的。如果只想匹配空格而不是\s或\h,则可以。我用它来匹配更广泛的whitspace chars。有人能解释为什么第四鸟的答案比我正确、准确、简洁的答案获得更多选票吗?在某一点上,他们都有1个紫外线,但由于某种未知的原因,他/她的答案是超前的,使研究人员偏离了我的答案。我知道我不是每个人都喜欢的人,但投票应该是答案,而不是回答者。如果UV来自于写出正则表达式分解,那么我很乐意编辑我的答案。@Stefano我想了解您发现第四鸟答案优于的度量标准。我使用regex101比较了这些模式,结果如下:他的第一个模式:[^\s=/]+/[^\s=]+=.*?=\h+[^\s=/]+/$,42个字符的模式,16个匹配,921个步骤;他的第二个模式:[^\W\]+?:[^\W\]+*/[^\s=]*=.*=。**=\h+[^\s=/]+/$,54个字符模式,16个匹配,1007个步骤;我的模式:[^=]+=.+??=$|[^=]+=,27个字符的模式,16个匹配,809个步骤,因此,我的模式被证明更高效、更简洁。有人能解释为什么第四鸟的答案比我正确、准确、简洁的答案获得更多选票吗?在某一点上,他们都有1个紫外线,但由于某种未知的原因,他/她的答案是超前的,使研究人员偏离了我的答案。我知道我不是每个人都喜欢的人,但投票应该是答案,而不是回答者。如果UV来自于写出正则表达式分解,那么我很乐意编辑我的答案。@Stefano我想了解您发现第四鸟答案优于的度量标准。我使用regex101比较了这些模式,结果如下:他的第一个模式:[^\s=/]+/[^\s=]+=.*?=\h+[^\s=/]+/$,42个字符的模式,16个匹配,921个步骤;他的第二个模式:[^\W\]+?:[^\W\]+*/[^\s=]*=.*=。**=\h+[^\s=/]+/$,54个字符模式,16个匹配,1007个步骤;我的模式:[^=]+=.+??=$|[^=]+=,27个字符的模式,16个匹配,809个步骤,因此,我的模式更高效、更简洁。
$msg = "ALARM_ID/I4=1010001 ALARM_STATE/U4=eventcode ALARM_TEXT/A=WMR_MAP_EXPORT LOTS/A[1]=[ STEFANO ] ALARM_STATE/U1=1 WAFER/U4=1 VI_KLARF_MAP/A=/test/klarf.map KLARF_STEPID/A=StepID KLARF_DEVICEID/A=DeviceID KLARF_EQUIPMENTID/A=EquipmentID KLARF_SETUP_ID/A=SetupID RULE_ID/U4=1234 RULE_FORMULA_EXPRESSION/A=a < b && c > d RULE_FORMULA_TEXT/A=1 < 0 && 2 > 3 RULE_FORMULA_RESULT/A=FAIL TIMESTAMP/A=10-Nov-2020 09:10:11 99999999";

preg_match_all('~([^=]+)=(.+?)(?=$| [^ =]+=)~', $msg, $out);
var_export(array_combine($out[1], $out[2]));
array (
  'ALARM_ID/I4' => '1010001',
  'ALARM_STATE/U4' => 'eventcode',
  'ALARM_TEXT/A' => 'WMR_MAP_EXPORT',
  'LOTS/A[1]' => '[ STEFANO ]',
  'ALARM_STATE/U1' => '1',
  'WAFER/U4' => '1',
  'VI_KLARF_MAP/A' => '/test/klarf.map',
  'KLARF_STEPID/A' => 'StepID',
  'KLARF_DEVICEID/A' => 'DeviceID',
  'KLARF_EQUIPMENTID/A' => 'EquipmentID',
  'KLARF_SETUP_ID/A' => 'SetupID',
  'RULE_ID/U4' => '1234',
  'RULE_FORMULA_EXPRESSION/A' => 'a < b && c > d',
  'RULE_FORMULA_TEXT/A' => '1 < 0 && 2 > 3',
  'RULE_FORMULA_RESULT/A' => 'FAIL',
  'TIMESTAMP/A' => '10-Nov-2020 09:10:11 99999999',
)