【问题标题】:Parse a formatted string into arrays of arrays将格式化的字符串解析为数组数组
【发布时间】:2011-02-09 16:00:04
【问题描述】:
+2-1+18*+7-21+3*-4-5+6x29

上面的字符串是我尝试拆分为 key => value 数组或类似字符串的示例。该字符串用于表示内网站点三栏页面上各种类的布局,用户可以通过拖放对其进行编辑。此字符串存储在 cookie 中以供下次访问时使用。

数字代表类的id,-+x代表类的状态(最小化、展开或隐藏),*代表分栏。

我可以使用explode轻松将其拆分为列,它给出了3个$key => $value关联的数组。

例如。

$column_layout = array( [0] => '+2-1+18' , [1] => '+7-21+3' , [2] => '-4-5+6x29' )

然后我需要从那里将其拆分为各个类,将状态和 id 保持在一起。由于不同的类和状态会因用户而异,以及每列有多少,所以我需要能够自动完成这一切。

$column1 = array(
    array( '+' , 2 ),
    array( '-' , 1 ),
    array( '+' , 18 )
);
$column2 = array(...

【问题讨论】:

  • 抱歉,澄清一下,我需要能够自动执行此操作。我应该提供更多关于如何使用它的信息,我现在已经完成了。
  • 澄清一下:$column1 = array( '+' => 2 , '-' => 1 , '+' => 18 ) 是不可能的结果,因为同一数组/子数组中的两个键可能不相同。
  • 我看到您的示例字符串有 3 个部分,由两个星号分隔。总是有3个部分吗?每个部分是否总是有 3 组值?
  • 这是我在 10 年后重读这篇文章后注意到的第一件事 ????很高兴知道这些年来我至少略有改善。老实说,我现在不记得这是什么了,我想当时它仅限于三个部分,但在每个部分中处理任意数量的部分和值​​可能是有意义的。我
  • Tbh,这可能是关于 SO 的一个过于具体的问题,而且标题也有点误导,但是哦,好吧,那时我对整个事情还比较陌生 ????

标签: php arrays string text-parsing formatted-input


【解决方案1】:

第一个 explode() 带有分隔符 * 的数组

然后您可以使用 preg_match_all 来匹配分解数组中的每个项目。这样的东西适用于您的示例输入。

$layout = explode('*', $input);
$columns = array();
foreach ( $layout as $item ){
    $parts = array();

    //matches either a -, x or + followed by one or more digits
    preg_match_all('/([+-x])(\d+)/', $item, $matches, PREG_SET_ORDER);

    foreach ( $matches as $match){ 
        //match[1] hold the + or -, match[2] holds the digits
        $parts[] = array($match[1], $match[2]);
    }
    $columns[] = $parts;
}

您示例的输出结果如下:

array(
     array( array('+', '2'), array('-', '1'), array('+', '18') ),
     array( array('+', '7'), array('-', '21'), array('+', '3') ),
     //etc
);

使用 PHP 5.3,您可以使用类似的东西(未经测试)。主要区别在于内部循环已被array_map 取代,这消除了对大量代码行的需求。 (数组映射将函数应用于数组中的每个项目并返回转换后的数组)。漂亮的closure syntax需要 PHP 5.3

$layout = explode('*', $input);
$columns = array();
foreach ( $layout as $item ){
    preg_match_all('/([+-x])(\d+)/', $item, $matches, PREG_SET_ORDER);
    $columns[] = array_map( function($a){ return array($a[1], $a[2]); },
                            $matches);
}

您也可以完全删除循环:

$innerMatch = function($item){
    preg_match_all('/([+-x])(\d+)/', $item, $matches, PREG_SET_ORDER);
    return array_map( function($a){ return array($a[1], $a[2]); },
                      $matches);
};
$columns = array_map($innerMatch, explode('*', $input));

但是,这有一个很大的缺点,就是对大多数 PHP 开发人员来说不是很容易阅读,这就是我不推荐使用它的原因。


更多解释

@Christopher Altman的要求

PHP 5.3 版本中唯一的新点真的是这样:

array_map(
          function($a){ return array($a[1], $a[2]); },
          $matches
);

稍微扩展和改变它(例如)

//bind an anonymous function to the variable $func
$func = function($a){
    return $a*$a; 
}; 
//$func() now calls the anonymous function we have just defined

//then we can call it like so:
$result = array_map($func, $myArray);

所以如果$myArray被定义为

array(1,2,3,4);

通过数组映射函数运行时可以认为是转换成

array(func(1),func(2),func(3),func(4));

但由于 PHP 不是 lazy language,所有函数一遇到就会被求值,所以数组从 array_map 返回为:

array(2, 4, 9, 16)

在实际代码中,preg_match_all 返回一个匹配数组(其中匹配是数组)。所以我所做的就是获取数组,并在每次匹配时应用一个函数,将匹配转换为所需格式的不同数组。

【讨论】:

  • 您能解释一下 PHP 5.3 示例是如何工作的吗?它看起来很强大,只是想快速分解每个部分。
  • 太好了,谢谢。这正是我所需要的。我曾尝试使用 preg_split,但它去掉了分隔符,这使事情变得复杂。
  • @Christopher 我添加了更多解释
  • @andyface preg_split() 有一个允许保留分隔符的标志。
【解决方案2】:

假设您严格格式化的输入具有静态数量的段和每个段的值,使用sscanf() 作为(详细)直接解析字符串的方法而不是preg_ 技术有一些优势。

  1. 这是一种直接的单功能技术。无需分解再解析。
  2. 此函数不会像preg_match() 那样生成无用的“全字符串匹配”。
  3. 您无需从$matches 数组中挑选出您需要的内容(如preg_match()
  4. 数值已经转换为整数(如果这对您有用的话)。

代码:(Demo)

$layout = '+2-1+18*+7-21+3*-4-5+6x29';

sscanf(
    $layout,
    '%[-+x]%d%[-+x]%d%[-+x]%d*%[-+x]%d%[-+x]%d%[-+x]%d*%[-+x]%d%[-+x]%d%[-+x]%d',
    $column1[0][0], $column1[0][1], $column1[1][0], $column1[1][1], $column1[2][0], $column1[2][1],
    $column2[0][0], $column2[0][1], $column2[1][0], $column2[1][1], $column2[2][0], $column2[2][1],
    $column3[0][0], $column3[0][1], $column3[1][0], $column3[1][1], $column3[2][0], $column3[2][1]
);

var_export($column1);
echo "\n---\n";
var_export($column2);
echo "\n---\n";
var_export($column3);

输出:

array (
  0 => 
  array (
    0 => '+',
    1 => 2,
  ),
  1 => 
  array (
    0 => '-',
    1 => 1,
  ),
  2 => 
  array (
    0 => '+',
    1 => 18,
  ),
)
---
array (
  0 => 
  array (
    0 => '+',
    1 => 7,
  ),
  1 => 
  array (
    0 => '-',
    1 => 21,
  ),
  2 => 
  array (
    0 => '+',
    1 => 3,
  ),
)
---
array (
  0 => 
  array (
    0 => '-',
    1 => 4,
  ),
  1 => 
  array (
    0 => '-',
    1 => 5,
  ),
  2 => 
  array (
    0 => '+',
    1 => 6,
  ),
)

附言

  • 如果您希望结果是具有 3 个一级元素的单个数组,并且这些元素包含 3 对符号数子数组,这也可以通过修改 sscanf() 中的参考变量来实现。
  • 如果您不喜欢格式字符串中的重复,您可以将重复的子模式声明为变量并以编程方式重复它(当然由星号分隔)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-05-10
    • 2017-02-02
    • 2014-12-22
    • 2018-02-07
    • 2016-11-04
    • 2013-09-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多