在PHP中解析数组的有效方法?

时间:2013-04-12 05:33:08

标签: php arrays

背景

我有一个数组,我是通过使用0d0a基于preg_split('/(?<=0d0a)(?!$)/')的每次出现来分割字符串来创建的。

例如:

$string = "78781110d0a78782220d0a";

将分为:

Array ( [0] => 78781110d0a [1] => 78782220d0a )  

有效的数组元素必须以7878开头,以0d0a结尾。

问题

但有时候,字符串中会有一个额外的0d0a,它会分成一个额外的无效数组元素,即不以7878开头。

以此字符串为例:

$string = "78781110d0a2220d0a78783330d0a";

这分为:

Array ( [0] => 78781110d0a [1] => 2220d0a [2] => 78783330d0a )

但实际上应该是:

Array ( [0] => 78781110d0a2220d0a [1] => 78783330d0a)

我的解决方案

我编写了以下(杂乱)代码来解决这个问题:

    $data = Array('78781110d0a','2220d0a','78783330d0a');
    $i = 0; //count for $data array;
    $j = 0; //count for $dataFixed array;
    $dataFixed = $data;

    foreach($data as $packet) {
        if (substr($packet,0,4) != "7878") { //if packet doesn't start with 7878, do some fixing
            if ($i != 0) { //its the first packet, can't help it!
                $j++;                    

                if ((substr(strtolower($packet), -4, 4) == "0d0a")) { //if the packet doesn't end with 0d0a, its 'mostly' not valid, so discard it
                    $dataFixed[$i-$j] = $dataFixed[$i-$j] . $packet;
                }
                    unset($dataFixed[$i-$j+1]);                        
                    $dataFixed = array_values($dataFixed);
            }
        }
        $i++;
    }

描述

我首先将数组复制到另一个数组$dataFixed。在foreach数组的$data循环中,我会检查它是否以7878开头。如果没有,我将其加入$data中的前一个数组。然后我取消设置$dataFixed中的当前数组并使用array_values重置数组元素。

但我对这个解决方案并不是很有信心。有更好,更有效的方法吗?

更新

如果输入字符串不像它应该的那样以0d0a结尾怎么办?它将坚持前一个数组元素..

例如:在字符串78781110d0a2220d0a78783330d0a0000中,0000应该被分隔为另一个数组元素。

3 个答案:

答案 0 :(得分:3)

使用其他positive lookahead (?=7878)来形成:

preg_split('/(?<=0d0a)(?=7878)/',$string)

注意:我删除了(?!$),因为根据您的示例数据,我不确定这是什么。

例如,此代码:

$string = "78781110d0a2220d0a78783330d0a";
$array  = preg_split('/(?<=0d0a)(?=7878)(?!$)/',$string);
print_r($array);

结果:

Array ( [0] => 78781110d0a2220d0a [1] => 78783330d0a )

<强>更新

根据您在输入字符串末尾添加可能的随机字符的修订问题,您可以添加三行来制作完整的程序:

$string = "78781110d0a2220d0a787830d0a330d0a0000";
$array  = preg_split('/(?<=0d0a)(?=7878)/',$string);
$temp = preg_split('/(7878.*0d0a)/',$array[count($array)-1],null,PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE);
$array[count($array)-1] = $temp[0];
if(count($temp)>1) { $array[] = $temp[1]; }
print_r($array);

我们基本上进行初始拆分,然后按照预期的数据格式拆分结果数组的最后一个元素,使用PREG_SPLIT_DELIM_CAPTURE保留分隔符。 PREG_SPLIT_NO_EMPTY确保如果输入字符串不以随机字符结尾,我们将不会获得空数组元素。

更新2:

根据您在下面的评论,您似乎暗示任何所需匹配项之间可能存在随机字符,并且您希望保留这些随机字符,您可以这样做:

$string = "0078781110d0a2220d0a2220d0a0000787830d0a330d0a000078781110d0a2220d0a0000787830d0a330d0a0000";
$split1 = preg_split('/(7878.*?0d0a)/',$string,null,PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE);
$result = array();
foreach($split1 as $e){
  $split2 = preg_split('/(.*0d0a)/',$e,null,PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE);
  foreach($split2 as $el){
    // test if $el doesn't start with 7878 and ends with 0d0a
    if(strpos($el,'7878') !== 0 && substr($el,-4) == '0d0a'){
    //if(preg_match('/^(?!7878).*0d0a$/',$el) === 1){
      $result[ count($result)-1 ] = $result[ count($result)-1 ] . $el;
    } else {
      $result[] = $el;
    }
  }
}
print_r($result);

此处采用的策略与上述不同。首先,我们使用nongreedy正则表达式.*?,根据与所需数据匹配的分隔符拆分输入字符串。此时我们有一些字符串包含所需值的结尾和最后的一些垃圾,所以我们再次根据最后一次出现的“0d0a”和贪婪的正则表达式.*0d0a进行拆分。然后我们将任何不以“7878”开头但以“0d0a”结尾的结果值附加到前一个值,因为它应该修复分裂的第一和第二半,因为它包含一个额外的“0d0a”。 / p>

我为最里面的if语句提供了两种方法,一种使用正则表达式。在我的测试中,正则表达式稍微慢一点,所以我留下了一个注释掉的。

我可能仍然没有满足您的全部要求,因此您必须告诉我它是否有效并且可能提供完整的数据集。

答案 1 :(得分:1)

为什么不使用preg_match_all?您可以避免所有非捕获组(向前看,向后看)以分割字符串(没有非捕获组删除匹配),只需找到您要查找的匹配项:

更新

<?php
$string = "00787817878110d0a22278780d0a78783330d0a00";
preg_match_all('/7878.*?0d0a(?=7878|[^(7878)]*?$)/', $string, $arr);
print_r($arr);
?>

提供数组$arr[0] => ( [0] => 787817878110d0a22278780d0a, [1] => 78783330d0a )。剥离前导和尾随垃圾字符(无论是以7878开头还是以78780d0a结尾。

所以$ arr [0]将是您正在寻找的值数组。

请参阅ideone

上的示例

使用多个7878值和多个0d0a值(即使这很荒谬)。

更新

如果拆分更符合您的风格,为什么不完全避免使用正则表达式?

<?php
$string = "787817878110d0a22278780d0a78783330d0a";
$arr = explode('0d0a7878', $string);
$string = implode('0d0a,7878', $arr);
$arr = explode(',', $string);
print_r($arr);
?>

这里我们用分隔符0d0a7878分割字符串,这是@ CharlieGorichanaz的解决方案正在做的事情,并为他提供快速,准确的解决方案。然后我们添加一个逗号,因为谁不喜欢逗号分隔的值?我们再次在逗号上explode获取所需值的数组。在性能方面,这应该比使用正则表达式更快。请参阅example

答案 2 :(得分:1)

我认为您使用的分隔符“0d0a”也恰好是内容的一部分!只要分隔符也可以成为内容的一部分,就不可能避免获取垃圾数据。不知何故,分隔符必须是唯一的。

可能的解决方案。

  • 将分隔符更改为不会作为数据的一部分出现的其他内容(000000,@!。;)
  • 如果您确定易于安排项目可能具有的文本长度,请使用它。根据例子,它是不可能的。

答案中给出的解决方案仅考虑您共享的样本数据。如果你想知道字符串的内容是什么,那么其他人给出的这些解决方案非常好用。否则这些解决方案无法保证您的保证!

最佳解决方案:修复右分隔符,然后使用正则表达式或爆炸您喜欢的任何内容。