@SilimAl
Telegram, VK, Instagram боты

Как разделить строку на части по количеству символов с переносом по словам?

Приветствую, коллеги!
Наведите на мысль, как разделить строку на части по количеству (не более) символов так чтоб не "резало" слова для записи в массив?

Пример: "Один Два Три Четыре Пять"
Делить, например, по 10
В результате:
Один Два(а не "Один Два Т")
Три Четыре
Пять

Есть функция:
function captionF($caption) {
    $encoding = 'UTF-8';
    $capI = 0;
    do {
        $captionarr[$capI] = mb_substr($caption, 0, 1024, $encoding);
//Понимаю что здесь еще раз нужно обработать элемент массив, но как именно.. застрял..
        $caption = str_replace($captionarr[$capI], '', $caption);
        $capI++;
    } while (mb_strlen($caption, $encoding) > 0);
    return $captionarr;
}


Пробовал искать "последний" пробел, но не вариант т.к. за ним могут остаться символы..
  • Вопрос задан
  • 2404 просмотра
Решения вопроса 1
@SilimAl Автор вопроса
Telegram, VK, Instagram боты
Коллеги, спасибо всем, кто не пожалел своего времени на заданный вопрос!
Не зря говорят "Утро вечера мудренее..", сегодня с утра осенило ))

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

В итоге мой конечный код выглядит так:

function captionF($caption) {
    $encoding = 'UTF-8';
    $needMAXlength = '1024'; //Обрезаем до.. символов
    $caption = $caption . ' '; //Добавляем в конце пробел чтоб "точно был"
    $capI = 0;
    do {
        $captionarr[$capI] = mb_substr($caption, 0, $needMAXlength, $encoding);
        $captionarr[$capI] = mb_strrchr($captionarr[$capI], ' ', TRUE, $encoding);
        $caption = str_replace($captionarr[$capI], '', $caption);
        $capI++;
    } while (mb_strlen($caption, $encoding) > 1); //При ">0" скрипт падал..
    return $captionarr;
}

Ilyas Sarsenbaev предложил немного альтернативный вариант, его также попробовал адаптировав под свои реалии:

function captionF($caption) {
    $encoding = 'UTF-8';
    $needMAXlength = '1024'; //Обрезаем до.. символов
    $caption = $caption . ' '; //Добавляем в конце пробел чтоб "точно был"
    $capI = 0;
    do {
        $captionarr[$capI] = mb_substr($caption, 0, $needMAXlength, $encoding);
        if ($caption[$needMAXlength] !== ' ') {
            $spacePosition = mb_strrpos($captionarr[$capI], ' ', $encoding);
            $captionarr[$capI] = mb_substr($caption, 0, $spacePosition, $encoding);
        }
        $caption = str_replace($captionarr[$capI], '', $caption);
        $capI++;
    } while (mb_strlen($caption, $encoding) > 1); //При ">0" скрипт падал..
    return $captionarr;
}

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

Еще раз спасибо всем участвовавшим в обсуждении.
Ответ написан
Пригласить эксперта
Ответы на вопрос 3
twobomb
@twobomb
А если слово длинее ограниченной длинны, всеравно придется резать. Вот пример
<?php
$s = "Один Два Три Четыре Пять Длинноеслово";
$pos = 0;
$maxLen = 10;
$res = [];
while($pos < mb_strlen($s)){
    while(mb_substr($s,$pos,1) == " ")
        $pos++;
        $v = mb_substr($s,$pos,$maxLen);
        if($pos+$maxLen <= mb_strlen($s)-1 && mb_substr($s,$pos+$maxLen,1) == " ")
            $v = mb_substr($s,$pos,$maxLen+1);
            
    $nextPos = mb_strrpos($v," ");
        if($nextPos != null && $pos+$maxLen < mb_strlen($s)-1){
            array_push($res,mb_substr($v,0,$nextPos));
            $pos+= $nextPos;
        }
        else{
            $pos+= mb_strlen($v);
            array_push($res,$v);
        }
 }
var_dump($res);

array(5) {
[0]=>
string(15) "Один Два"
[1]=>
string(19) "Три Четыре"
[2]=>
string(8) "Пять"
[3]=>
string(20) "Длинноесло"
[4]=>
string(4) "во"
}
Ответ написан
Комментировать
nokimaro
@nokimaro
Меня невозможно остановить, если я смогу начать.
Ровно эту задачу можно решить функцией wordwrap - https://www.php.net/wordwrap
Но она не корректно работает с юникод строками

В итоге быстрое гугление предлагает такие полифилы
<?php
function mb_wordwrap($string, $width=75, $break="\n", $cut = false) {
    if (!$cut) {
        $regexp = '#^(?:[\x00-\x7F]|[\xC0-\xFF][\x80-\xBF]+){'.$width.',}\b#U';
    } else {
        $regexp = '#^(?:[\x00-\x7F]|[\xC0-\xFF][\x80-\xBF]+){'.$width.'}#';
    }
    $string_length = mb_strlen($string,'UTF-8');
    $cut_length = ceil($string_length / $width);
    $i = 1;
    $return = '';
    while ($i < $cut_length) {
        preg_match($regexp, $string,$matches);
        $new_string = $matches[0];
        $return .= $new_string.$break;
        $string = substr($string, strlen($new_string));
        $i++;
    }
    return $return.$string;
}


<?php
function mb_wordwrap($str, $width = 74, $break = "\r\n", $cut = false)
        {
            return preg_replace(
                '~(?P<str>.{' . $width . ',}?' . ($cut ? '(?(?!.+\s+)\s*|\s+)' : '\s+') . ')(?=\S+)~mus',
                '$1' . $break,
                $str
            );
        }
Ответ написан
Spartak-2205
@Spartak-2205
Разработка и создание сайтов
function mbCutString($str, $length, $encoding = 'UTF-8')
{
	$arr = array();

	while(mb_strlen($str, $encoding) > 0) {
		if (mb_strlen($str, $encoding) <= $length) {
			array_push($arr, trim($str));
			return $arr;
		}

		$tmp = mb_substr($str, 0, $length, $encoding);
		$res = trim(mb_substr($tmp, 0, mb_strripos($tmp, ' ', 0, $encoding), $encoding));
		$str = mb_strlen($res, $encoding) ? trim(str_ireplace($res, "", $str)) : false;
		if ($res) array_push($arr, $res);
	}

	return $arr;
}
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы