用于修复损坏的序列化PHP数据的正则表达式/代码。

时间:2010-06-30 11:23:28

标签: php

我有一个由PHP序列化的大规模多维数组。它已存储在MySQL中,并且数据字段不够大......结尾已被切断......我需要提取数据...... unserialize不会工作......有没有人知道一个可以关闭所有数组的代码......重新计算字符串长度......手动数据太多了。

非常感谢。

12 个答案:

答案 0 :(得分:32)

这是重新计算序列化数组中元素的长度:

$fixed = preg_replace_callback(
    '/s:([0-9]+):\"(.*?)\";/',
    function ($matches) { return "s:".strlen($matches[2]).':"'.$matches[2].'";';     },
    $serialized
);

但是,如果您的字符串包含";,则无效。在这种情况下,无法自动修复序列化数组字符串 - 需要手动编辑。

答案 1 :(得分:13)

我已尝试过这篇文章中发现的所有内容,但对我来说没有任何作用。经过几个小时的痛苦,我在google的深层页面找到了最终的工作:

function fix_str_length($matches) {
    $string = $matches[2];
    $right_length = strlen($string); // yes, strlen even for UTF-8 characters, PHP wants the mem size, not the char count
    return 's:' . $right_length . ':"' . $string . '";';
}
function fix_serialized($string) {
    // securities
    if ( !preg_match('/^[aOs]:/', $string) ) return $string;
    if ( @unserialize($string) !== false ) return $string;
    $string = preg_replace("%\n%", "", $string);
    // doublequote exploding
    $data = preg_replace('%";%', "µµµ", $string);
    $tab = explode("µµµ", $data);
    $new_data = '';
    foreach ($tab as $line) {
        $new_data .= preg_replace_callback('%\bs:(\d+):"(.*)%', 'fix_str_length', $line);
    }
    return $new_data;
}

您按如下方式调用例行程序:

//Let's consider we store the serialization inside a txt file
$corruptedSerialization = file_get_contents('corruptedSerialization.txt');

//Try to unserialize original string
$unSerialized = unserialize($corruptedSerialization);

//In case of failure let's try to repair it
if(!$unSerialized){
    $repairedSerialization = fix_serialized($corruptedSerialization);
    $unSerialized = unserialize($repairedSerialization);
}

//Keep your fingers crossed
var_dump($unSerialized);

答案 2 :(得分:7)

解决方案:

1)尝试在线:

Serialized String Fixer (online tool)

2)使用功能:

unserialize( serialize_corrector( $serialized_string ) ) ;

代码:

function serialize_corrector($serialized_string){
    // at first, check if "fixing" is really needed at all. After that, security checkup.
    if ( @unserialize($serialized_string) !== true &&  preg_match('/^[aOs]:/', $serialized_string) ) {
         $serialized_string = preg_replace_callback( '/s\:(\d+)\:\"(.*?)\";/s',    function($matches){return 's:'.strlen($matches[2]).':"'.$matches[2].'";'; },   $serialized_string );
    }
    return $serialized_string;
} 

答案 3 :(得分:2)

使用preg_replace_callback()代替preg_replace(.../e)(因为/e修饰符为deprecated)。

$fixed_serialized_String = preg_replace_callback('/s:([0-9]+):\"(.*?)\";/',function($match) {
    return "s:".strlen($match[2]).':"'.$match[2].'";';
}, $serializedString);

$correct_array= unserialize($fixed_serialized_String);

答案 4 :(得分:1)

以下代码段会尝试阅读&解析递归损坏的序列化字符串(blob数据)。例如,如果您存储到数据库列字符串太长并且它被切断了。保证数字基元和布尔有效,字符串可能被切断和/或数组键可能丢失。该例程可能是有用的,例如如果恢复重要(不是全部)数据部分是您的充分解决方案。

class Unserializer
{
    /**
    * Parse blob string tolerating corrupted strings & arrays
    * @param string $str Corrupted blob string
    */
    public static function parseCorruptedBlob(&$str)
    {
        // array pattern:    a:236:{...;}
        // integer pattern:  i:123;
        // double pattern:   d:329.0001122;
        // boolean pattern:  b:1; or b:0;
        // string pattern:   s:14:"date_departure";
        // null pattern:     N;
        // not supported: object O:{...}, reference R:{...}

        // NOTES:
        // - primitive types (bool, int, float) except for string are guaranteed uncorrupted
        // - arrays are tolerant to corrupted keys/values
        // - references & objects are not supported
        // - we use single byte string length calculation (strlen rather than mb_strlen) since source string is ISO-8859-2, not utf-8

        if(preg_match('/^a:(\d+):{/', $str, $match)){
            list($pattern, $cntItems) = $match;
            $str = substr($str, strlen($pattern));
            $array = [];
            for($i=0; $i<$cntItems; ++$i){
                $key = self::parseCorruptedBlob($str);
                if(trim($key)!==''){ // hmm, we wont allow null and "" as keys..
                    $array[$key] = self::parseCorruptedBlob($str);
                }
            }
            $str = ltrim($str, '}'); // closing array bracket
            return $array;
        }elseif(preg_match('/^s:(\d+):/', $str, $match)){
            list($pattern, $length) = $match;
            $str = substr($str, strlen($pattern));
            $val = substr($str, 0, $length + 2); // include also surrounding double quotes
            $str = substr($str, strlen($val) + 1); // include also semicolon
            $val = trim($val, '"'); // remove surrounding double quotes
            if(preg_match('/^a:(\d+):{/', $val)){
                // parse instantly another serialized array
                return (array) self::parseCorruptedBlob($val);
            }else{
                return (string) $val;
            }
        }elseif(preg_match('/^i:(\d+);/', $str, $match)){
            list($pattern, $val) = $match;
            $str = substr($str, strlen($pattern));
            return (int) $val;
        }elseif(preg_match('/^d:([\d.]+);/', $str, $match)){
            list($pattern, $val) = $match;
            $str = substr($str, strlen($pattern));
            return (float) $val;
        }elseif(preg_match('/^b:(0|1);/', $str, $match)){
            list($pattern, $val) = $match;
            $str = substr($str, strlen($pattern));
            return (bool) $val;
        }elseif(preg_match('/^N;/', $str, $match)){
            $str = substr($str, strlen('N;'));
            return null;
        }
    }
}

// usage:
$unserialized = Unserializer::parseCorruptedBlob($serializedString);

答案 5 :(得分:0)

基于@Emil M答案 这是一个固定版本,适用于包含双引号的文本。

function fix_broken_serialized_array($match) {
    return "s:".strlen($match[2]).":\"".$match[2]."\";"; 
}
$fixed = preg_replace_callback(
    '/s:([0-9]+):"(.*?)";/',
    "fix_broken_serialized_array",
    $serialized
);

答案 6 :(得分:0)

最适合我的解决方案:

$output_array = unserialize(My_checker($serialized_string));

代码:

function My_checker($serialized_string){
    // securities
    if (empty($serialized_string))                      return '';
    if ( !preg_match('/^[aOs]:/', $serialized_string) ) return $serialized_string;
    if ( @unserialize($serialized_string) !== false ) return $serialized_string;

    return
    preg_replace_callback(
        '/s\:(\d+)\:\"(.*?)\";/s', 
        function ($matches){  return 's:'.strlen($matches[2]).':"'.$matches[2].'";';  },
        $serialized_string )
    ;
}

答案 7 :(得分:0)

结论:-) 在3天(而不是2个估计的小时)内,将祝福的WordPress网站迁移到一个新域名后,我终于找到了该页面! 同事们,请把它当作我对所有答案的“谢谢”(Thank_You_Very_Much_Indeed)。 下面的代码由您所有的解决方案组成,几乎没有添加。 杰斐依(JFYI):对我个人而言,解决方案3最为有效。卡玛勒·萨利赫(Kamal Saleh)-你是最好的!!

DECLARE @list varchar(max) = '';
DECLARE @comma varchar(2) = '';
SELECT @list = @list + @comma + County, @comma = ', ' FROM County
print @list

答案 8 :(得分:-2)

我怀疑是否有人会编写代码来检索部分保存的数组:) 我修理过这样的东西,但只需要手工操作,花了几个小时,然后我意识到我不需要阵列的那一部分......

除非它真正重要的数据(我的意思是非常重要),否则你最好离开这一行

答案 9 :(得分:-2)

您可以通过数组将无效的序列化数据恢复正常:)

str = "a:1:{i:0;a:4:{s:4:\"name\";s:26:\"20141023_544909d85b868.rar\";s:5:\"dname\";s:20:\"HTxRcEBC0JFRWhtk.rar\";s:4:\"size\";i:19935;s:4:\"dead\";i:0;}}"; 

preg_match_all($re, $str, $matches);

if(is_array($matches) && !empty($matches[1]) && !empty($matches[2]))
{
    foreach($matches[1] as $ksel => $serv)
    {
        if(!empty($serv))
        {
            $retva[] = $serv;
        }else{
            $retva[] = $matches[2][$ksel];
        }
    }

    $count = 0;
    $arrk = array();
    $arrv = array();
    if(is_array($retva))
    {
        foreach($retva as $k => $va)
        {
            ++$count;
            if($count/2 == 1)
            {
                $arrv[] = $va;
                $count = 0;
            }else{
                $arrk[] = $va;
            }
        }
        $returnse = array_combine($arrk,$arrv);
    }

}

print_r($returnse);

答案 10 :(得分:-3)

序列化几乎总是很糟糕,因为你无法以任何方式搜索它。对不起,但好像你已经回到了角落......

答案 11 :(得分:-5)

我认为这几乎是不可能的。 在修复阵列之前,您需要知道它是如何损坏的。 有多少孩子失踪?内容是什么?

抱歉,你不能这样做。

证明:

<?php

$serialized = serialize(
    [
        'one'   => 1,
        'two'   => 'nice',
        'three' => 'will be damaged'
    ]
);

var_dump($serialized); // a:3:{s:3:"one";i:1;s:3:"two";s:4:"nice";s:5:"three";s:15:"will be damaged";}

var_dump(unserialize('a:3:{s:3:"one";i:1;s:3:"two";s:4:"nice";s:5:"tee";s:15:"will be damaged";}')); // please note 'tee'

var_dump(unserialize('a:3:{s:3:"one";i:1;s:3:"two";s:4:"nice";s:5:"three";s:')); // serialized string is truncated

链接:https://ideone.com/uvISQu

即使您可以重新计算键/值的长度,也不能信任从此源检索的数据,因为您无法重新计算这些值的值。例如。如果序列化数据是对象,则不再可以访问您的属性。