PHP preg_split 使用分隔符作为数组键
PHP preg_split use delimiter as array keys
我需要用正则表达式分隔符拆分字符串,但需要分隔符作为数组键。
这是一个示例字符串:
*01the title*35the author*A7other useless infos*AEother useful infos*AEsome delimiters can be there multiple times
分隔符是星号 (*) 后跟两个字母数字字符。
我使用这个正则表达式模式:/\*[A-Z0-9]{2}/
这是我的 preg_split 电话:
$attributes = preg_split('/\*[A-Z0-9]{2}/', $line);
这可行,但我需要每个匹配的定界符作为关联数组中值的键。
我得到的是这样的:
$matches = [
0 => 'the title',
1 => 'the author',
2 => 'other useless infos',
3 => 'other useful infos',
4 => 'some delimiters can be there multiple times'
];
它应该是这样的:
$matches = [
'*01' => 'the title',
'*35' => 'the author',
'*A7' => 'other useless infos',
'*AE' => [
'other useful infos',
'some delimiters can be there multiple times',
],
];
有人对如何实现这一目标有任何建议吗?
使用 preg_split
函数的 PREG_SPLIT_DELIM_CAPTURE
标志也可以获取捕获的定界符(参见 documentation)。
所以在你的情况下:
# The -1 is the limit parameter (no limit)
$attributes = preg_split('/(\*[A-Z0-9]{2})/', $line, -1, PREG_SPLIT_DELIM_CAPTURE);
现在你有 $attributes
的元素 0
作为第一个分隔符之前的所有元素,然后交替捕获的分隔符和下一组,这样你就可以像这样构建你的 $matches
数组(假设您不想保留第一组):
for($i=1; $i<sizeof($attributes)-1; $i+=2){
$matches[$attributes[$i]] = $attributes[$i+1];
}
为了解决分隔符多次出现的问题,您可以调整 for 循环内的行以检查此键是否已存在,如果出现这种情况,则创建一个数组。
编辑:必要时创建数组的可能性是使用此代码:
for($i=1; $i<sizeof($attributes)-1; $i+=2){
$key = $attributes[$i];
if(array_key_exists($key, $matches)){
if(!is_array($matches[$key]){
$matches[$key] = [$matches[$key]];
}
array_push($matches[$key], $attributes[$i+1]);
} else {
$matches[$attributes[$i]] = $attributes[$i+1];
}
}
当然可以简化下游代码,特别是如果您将所有值都放在(可能是单个元素)数组中。
您可以将键匹配并捕获到第 1 组中,并将下一个定界符之前的所有文本匹配并捕获到第 2 组中,其中定界符与第一个捕获的定界符不同。然后,在一个循环中,检查所有键和值,并在出现一次或多次的分隔符模式中拆分这些值。
正则表达式是
(\*[A-Z0-9]{2})(.*?)(?=(?!)\*[A-Z0-9]{2}|$)
参见regex demo。
详情
(\*[A-Z0-9]{2})
- 分隔符,第 1 组:一个 *
和两个大写字母或数字
(.*?)
- 值,第 2 组:除换行字符外的任何 0+ 个字符,尽可能少
(?=(?!)\*[A-Z0-9]{2}|$)
- 直到分隔符模式 (\*[A-Z0-9]{2}
) 不等于第 1 组 ((?!)
) 中捕获的文本或字符串结尾 ($
).
见PHP demo:
$re = '/(\*[A-Z0-9]{2})(.*?)(?=(?!)\*[A-Z0-9]{2}|$)/';
$str = '*01the title*35the author*A7other useless infos*AEother useful infos*AEsome delimiters can be there multiple times';
$res = [];
if (preg_match_all($re, $str, $m, PREG_SET_ORDER, 0)) {
foreach ($m as $kvp) {
$tmp = preg_split('~\*[A-Z0-9]+~', $kvp[2]);
if (count($tmp) > 1) {
$res[$kvp[1]] = $tmp;
} else {
$res[$kvp[1]] = $kvp[2];
}
}
print_r($res);
}
输出:
Array
(
[*01] => the title
[*35] => the author
[*A7] => other useless infos
[*AE] => Array
(
[0] => other useful infos
[1] => some delimiters can be there multiple times
)
)
好的,我回答了我自己关于如何处理多个相同分隔符的问题。
感谢@markus-ankenbrand 的开始:
$attributes = preg_split('/(\*[A-Z0-9]{2})/', $line, -1, PREG_SPLIT_DELIM_CAPTURE);
$matches = [];
for ($i = 1; $i < sizeof($attributes) - 1; $i += 2) {
if (isset($matches[$attributes[$i]]) && is_array($matches[$attributes[$i]])) {
$matches[$attributes[$i]][] = $attributes[$i + 1];
} elseif (isset($matches[$attributes[$i]]) && !is_array($matches[$attributes[$i]])) {
$currentValue = $matches[$attributes[$i]];
$matches[$attributes[$i]] = [$currentValue];
$matches[$attributes[$i]][] = $attributes[$i + 1];
} else {
$matches[$attributes[$i]] = $attributes[$i + 1];
}
}
胖 if/else 语句看起来不太好,但它做了它需要做的事情。
更简单、更清晰的代码是
preg_match_all("...", $text, $keyGroups);
$valueGroups = preg_split("...", $text);
$attributes = array_combine($keyGroups[1], $valueGroups);
我需要用正则表达式分隔符拆分字符串,但需要分隔符作为数组键。
这是一个示例字符串:
*01the title*35the author*A7other useless infos*AEother useful infos*AEsome delimiters can be there multiple times
分隔符是星号 (*) 后跟两个字母数字字符。
我使用这个正则表达式模式:/\*[A-Z0-9]{2}/
这是我的 preg_split 电话:
$attributes = preg_split('/\*[A-Z0-9]{2}/', $line);
这可行,但我需要每个匹配的定界符作为关联数组中值的键。
我得到的是这样的:
$matches = [
0 => 'the title',
1 => 'the author',
2 => 'other useless infos',
3 => 'other useful infos',
4 => 'some delimiters can be there multiple times'
];
它应该是这样的:
$matches = [
'*01' => 'the title',
'*35' => 'the author',
'*A7' => 'other useless infos',
'*AE' => [
'other useful infos',
'some delimiters can be there multiple times',
],
];
有人对如何实现这一目标有任何建议吗?
使用 preg_split
函数的 PREG_SPLIT_DELIM_CAPTURE
标志也可以获取捕获的定界符(参见 documentation)。
所以在你的情况下:
# The -1 is the limit parameter (no limit)
$attributes = preg_split('/(\*[A-Z0-9]{2})/', $line, -1, PREG_SPLIT_DELIM_CAPTURE);
现在你有 $attributes
的元素 0
作为第一个分隔符之前的所有元素,然后交替捕获的分隔符和下一组,这样你就可以像这样构建你的 $matches
数组(假设您不想保留第一组):
for($i=1; $i<sizeof($attributes)-1; $i+=2){
$matches[$attributes[$i]] = $attributes[$i+1];
}
为了解决分隔符多次出现的问题,您可以调整 for 循环内的行以检查此键是否已存在,如果出现这种情况,则创建一个数组。
编辑:必要时创建数组的可能性是使用此代码:
for($i=1; $i<sizeof($attributes)-1; $i+=2){
$key = $attributes[$i];
if(array_key_exists($key, $matches)){
if(!is_array($matches[$key]){
$matches[$key] = [$matches[$key]];
}
array_push($matches[$key], $attributes[$i+1]);
} else {
$matches[$attributes[$i]] = $attributes[$i+1];
}
}
当然可以简化下游代码,特别是如果您将所有值都放在(可能是单个元素)数组中。
您可以将键匹配并捕获到第 1 组中,并将下一个定界符之前的所有文本匹配并捕获到第 2 组中,其中定界符与第一个捕获的定界符不同。然后,在一个循环中,检查所有键和值,并在出现一次或多次的分隔符模式中拆分这些值。
正则表达式是
(\*[A-Z0-9]{2})(.*?)(?=(?!)\*[A-Z0-9]{2}|$)
参见regex demo。
详情
(\*[A-Z0-9]{2})
- 分隔符,第 1 组:一个*
和两个大写字母或数字(.*?)
- 值,第 2 组:除换行字符外的任何 0+ 个字符,尽可能少(?=(?!)\*[A-Z0-9]{2}|$)
- 直到分隔符模式 (\*[A-Z0-9]{2}
) 不等于第 1 组 ((?!)
) 中捕获的文本或字符串结尾 ($
).
见PHP demo:
$re = '/(\*[A-Z0-9]{2})(.*?)(?=(?!)\*[A-Z0-9]{2}|$)/';
$str = '*01the title*35the author*A7other useless infos*AEother useful infos*AEsome delimiters can be there multiple times';
$res = [];
if (preg_match_all($re, $str, $m, PREG_SET_ORDER, 0)) {
foreach ($m as $kvp) {
$tmp = preg_split('~\*[A-Z0-9]+~', $kvp[2]);
if (count($tmp) > 1) {
$res[$kvp[1]] = $tmp;
} else {
$res[$kvp[1]] = $kvp[2];
}
}
print_r($res);
}
输出:
Array
(
[*01] => the title
[*35] => the author
[*A7] => other useless infos
[*AE] => Array
(
[0] => other useful infos
[1] => some delimiters can be there multiple times
)
)
好的,我回答了我自己关于如何处理多个相同分隔符的问题。 感谢@markus-ankenbrand 的开始:
$attributes = preg_split('/(\*[A-Z0-9]{2})/', $line, -1, PREG_SPLIT_DELIM_CAPTURE);
$matches = [];
for ($i = 1; $i < sizeof($attributes) - 1; $i += 2) {
if (isset($matches[$attributes[$i]]) && is_array($matches[$attributes[$i]])) {
$matches[$attributes[$i]][] = $attributes[$i + 1];
} elseif (isset($matches[$attributes[$i]]) && !is_array($matches[$attributes[$i]])) {
$currentValue = $matches[$attributes[$i]];
$matches[$attributes[$i]] = [$currentValue];
$matches[$attributes[$i]][] = $attributes[$i + 1];
} else {
$matches[$attributes[$i]] = $attributes[$i + 1];
}
}
胖 if/else 语句看起来不太好,但它做了它需要做的事情。
更简单、更清晰的代码是
preg_match_all("...", $text, $keyGroups);
$valueGroups = preg_split("...", $text);
$attributes = array_combine($keyGroups[1], $valueGroups);