PHP mb_split(), захват разделителей

preg_split имеет дополнительный PREG_SPLIT_DELIM_CAPTURE флаг, который также возвращает все разделители в возвращаемом массиве. mb_split не.

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

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

Решение Благодаря пользователю Casimir et Hippolyte я создал решение и разместил его на 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;
}

1 ответ

Решение

Захват разделителей возможен только с 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с осторожностью. По определению, обходные пути являются утверждениями нулевой ширины и не соответствуют никаким символам, а только позициям в строке. Таким образом, вы можете использовать этот тип шаблона, который соответствует позициям после или перед разделителем:

(?=delimiter)|(<=delimiter)

(Ограничение этого способа заключается в том, что подшаблон во внешнем виде не может иметь переменную длину (другими словами, вы не можете использовать квантификатор внутри), но это может быть чередование подшаблонов фиксированной длины:(?<=subpat1|subpat2|subpat3))

Другие вопросы по тегам