2015-06-02 8 views
3

preg_split имеет необязательный флаг PREG_SPLIT_DELIM_CAPTURE, который также возвращает все разделители в возвращаемом массиве. mb_split нет.PHP mb_split(), фиксирующие разделители

Есть ли способ разделить многобайтовую строку (а не только UTF-8, но все виды) и захватить разделители?

Я пытаюсь создать многобилетный разделитель разломов, сохраняя разрывы строк, но предпочел бы более универсальное решение.

Solution Благодаря пользовательским Казимир и Ипполит, я построил решение и разместил его на 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) преобразовать строку в UTF8, используйте preg_split с PREG_SPLIT_DELIM_CAPTURE и использовать array_map конвертировать каждый из элементов в исходной кодировке.

Этот способ является более простым. Это не так во втором случае. (Заметим, что в общем, это более простой, чтобы всегда работать в UTF8, вместо того чтобы справиться с экзотическими кодировок)

2) вместо разделенным как функции вы должны использовать, например mb_ereg_search_regs чтобы получить совпавшие части и построить картину так:

delimiter|all_that_is_not_the_delimiter 

(Обратите внимание, что две ветви чередовании должны быть взаимоисключающими и заботиться, чтобы написать их таким образом, что делает невозможным промежутки между результатами. Первая часть должна быть в начале строки и последней части должны быть в конце. Каждая часть должна быть смежной с предыдущей и так далее.)

3) использование mb_split с lookarounds. По определению обратные являются утверждениями с нулевой шириной и не соответствуют никаким символам, а только позициям в строке. Таким образом, вы можете использовать этот вид шаблона, который соответствует позиции после или перед разделителем:

(?=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