2015-06-02 86 views
3

preg_split有一个可选的PREG_SPLIT_DELIM_CAPTURE标志,它也返回返回数组中的所有分隔符。 mb_split没有。PHP mb_split(),捕获分隔符

有没有什么办法来拆分多字节字符串(不只是UTF-8,但所有类型)和捕获分隔符?

我正在尝试制作一个多字节安全的换行符,保留换行符,但希望使用更具代表性的可用解决方案。

解决方案 由于用户卡西米尔等伊波利特,我建立了一个解决方案,并张贴在GitHub上 (https://github.com/vanderlee/PHP-multibyte-functions/blob/master/functions/mb_explode.php),这使得所有的使preg_split标志:

/** 
* A cross between mb_split and preg_split, adding the preg_split flags 
* to mb_split. 
* @param string $pattern 
* @param string $string 
* @param int $limit 
* @param int $flags 
* @return array 
*/ 
function mb_explode($pattern, $string, $limit = -1, $flags = 0) {  
    $strlen = strlen($string);  // bytes! 
    mb_ereg_search_init($string); 

    $lengths = array(); 
    $position = 0; 
    while (($array = mb_ereg_search_pos($pattern)) !== false) { 
     // capture split 
     $lengths[] = array($array[0] - $position, false, null); 

     // move position 
     $position = $array[0] + $array[1]; 

     // capture delimiter 
     $regs = mb_ereg_search_getregs();   
     $lengths[] = array($array[1], true, isset($regs[1]) && $regs[1]); 

     // Continue on? 
     if ($position >= $strlen) { 
      break; 
     }   
    } 

    // Add last bit, if not ending with split 
    $lengths[] = array($strlen - $position, false, null); 

    // Substrings 
    $parts = array(); 
    $position = 0;  
    $count = 1; 
    foreach ($lengths as $length) { 
     $is_delimiter = $length[1]; 
     $is_captured = $length[2]; 

     if ($limit > 0 && !$is_delimiter && ($length[0] || ~$flags & PREG_SPLIT_NO_EMPTY) && ++$count > $limit) { 
      if ($length[0] > 0 || ~$flags & PREG_SPLIT_NO_EMPTY) {   
       $parts[] = $flags & PREG_SPLIT_OFFSET_CAPTURE 
          ? array(mb_strcut($string, $position), $position) 
          : mb_strcut($string, $position);     
      } 
      break; 
     } elseif ((!$is_delimiter || ($flags & PREG_SPLIT_DELIM_CAPTURE && $is_captured)) 
       && ($length[0] || ~$flags & PREG_SPLIT_NO_EMPTY)) { 
      $parts[] = $flags & PREG_SPLIT_OFFSET_CAPTURE 
         ? array(mb_strcut($string, $position, $length[0]), $position) 
         : mb_strcut($string, $position, $length[0]); 
     } 

     $position += $length[0]; 
    } 

    return $parts; 
} 
+0

你想做什么?发布一个示例字符串。 –

回答

2

捕获分隔符是唯一可能与preg_split并不适用于其他功能。

那么三种可能性:

1)您的字符串转换为UTF-8,使用preg_splitPREG_SPLIT_DELIM_CAPTURE,并使用array_map每个项目转换成原始编码。

这种方式更简单。第二种方式并非如此。 (请注意,在一般情况下,它更简单,在UTF8总是工作,而不是具有异国情调的编码处理,)代替分裂样功能,你需要使用例如mb_ereg_search_regs

2)获得匹配的零部件,并建立这样的模式:

delimiter|all_that_is_not_the_delimiter 

(注意交替的两个分支必须是相互排斥的,照顾到他们写的方式,使得结果之间是不可能的差距。第一部分必须在一开始的字符串和最后一部分必须在最后。每个部分必须是连续的到先前等等。)

3)使用mb_splitlookarounds。根据定义,lookaround是零宽度断言,不匹配任何字符,但只匹配字符串中的位置。所以,你可以使用这种模式,经过或分隔符之前匹配的位置:

(?=delimiter)|(<=delimiter) 

(这种方式的局限性是,在回顾后的子模式不能具有可变长度(换句话说,你不能在里面使用量词),但它可以是固定长度子模式的交替:(?<=subpat1|subpat2|subpat3)

+0

我想用它来分割线条上的线条。方法3表现得很好:'mb_split('(?= \ r \ n | \ r | \ n)|(<= \ r \ n | \ r | \ n)',$ text);'。谢谢! – Martijn

+0

@Martijn:如果换行符序列是'\ r \ n',这种方式将不起作用,因为该模式将在\ r和\ n处进行分割。所以你会得到:'line','\ r','\ n','line'。方法2)在这种情况下更合适,因为您可以简单地使用这种模式:'[^ \ r \ n] + | \ r?\ n | \ r' –

+0

好吧,它似乎在我的测试中工作,但也有PHP 5.2和5.3抛出错误的问题,因为他们认为模式是空的。我会接下来看看你的解决方案2。 – Martijn