Pager текста: нетривиально побить текст на страницы

kvn

programmer
Pager текста: нетривиально побить текст на страницы

Hi, уважаемые.

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

Все прекрасно работает, если это просто текст.
Но!
Shit начинается тогода, когда в текст начинают прописывать теги (ссылки), типа <a href='bla bla bla'>bla</a>.

Здесь возникает ситуевина, что текст может порезаться на половине тега.

Существуют ли какие-то стандартные решения для избежания таких ситуаций.

Очень приветствуются куски кода/ссылки, или просто теория, т.к. вопрос горящий.

Надеюсь на понимание и помощь.
Спаисбо.
 

kvn

programmer
вставлял в текст [PAGE] и по нему бил на страницы.
Дело в том, что текст разбивается на страницы динамически от входящих условий.
Видел в поиске разбивку текста по указанному размеру в Кб:
http://phpclub.ru/talk/showthread.php?s=&threadid=27871&highlight=%F0%E0%E7%E1%E8%F2%FC+%ED%E0+%F1%F2%F0%E0%ED%E8%F6%FB

Но это не решает проблемы:
Shit начинается тогда, когда в текст начинают прописывать теги (ссылки), типа <a href='bla bla bla'>bla</a>.

Здесь возникает ситуевина, что текст может порезаться на половине тега.
 

zarus

Хитрожопый макак
PHP:
// divider.php
// Made by ZarusJamer 14/11/2005
// (c) Нефиг тырить чужое, поимейте совесть
function subtract_string($str,&$pre) {
// Вспомогательная функция деления длинной непрерывной строки
// $str - строка для разбивки
// $pre - смещение от предыдущей строки
  global $divider;
  $out = '';
  // Если переданная строка - тэг, то добавляем к строке без изменения смещения
  if (preg_match('/^(<.+?>)$/si',$str)) {
    $out .= $str;
  }
  else {
    $size = strlen($str);
    // Если длина строки со смещением меньше заданной максимальной длины - добавляем без изменений и увеличиваем смещение
    if ($size <= $divider['max']-$pre) {
      $out .= $str;
      $pre += $size;
    }
    else {
      $new_len = $divider['max'] - $pre;
      // Добавляем часть строки и разделитель строк
      $out .= substr($str,0,$new_len).$divider['text'];
      $pre = 0;
      // Разбиваем оставшуюся часть строки на составляющие
      $out .= subtract_string(substr($str,$new_len),$pre);
    }
  }
  return $out;
}
function split_string($str) {
// Основная функция деления вводимой строки
  global $divider;
  $out = '';
  $pre = 0;
  // Разбиваем строку на подчасти по символам-резделителям.
  // explode(' '...) не вызывается поскольку непарные тэги могут содержать пробелы
  while (preg_match('/((?:\s|^)(?:[^\s](?:<.+?>)?)+?(?:\s|$))/si',$str,$arr)) {
    // Проверка длины подчасти строки без тэгов
    if (strlen(strip_tags($arr[1])) > $divider['max']) {
      // Разбиваем подчасть на мелкие части - массив обычных строк и тэгов.
      $var = preg_split('/(<.+?>)/si',$arr[1],-1,PREG_SPLIT_DELIM_CAPTURE);
      foreach ($var as $val) {
        $out .= subtract_string($val,$pre);
      }
      // Удаляем последний разделитель длинных строк
      $out = preg_replace('/^(.*?)'.str_replace('/','\/',($divider['text'])).'$/si','\1',$out);
    }
    else {
      $out .= $arr[1];
    }
    $str = str_replace($arr[0],'',$str);
  }
  return $out;
}

$str['in'] = 'Съешь ещё этих мягких французских булочек!<br />Ну, пожаа<b>аааааааааааааааааааааааааааааааааааа</b>ааааааааааааалуйста!';
$str['out1'] = '';
// Текст разделителя - можно подставить любой.
$divider['text'] = '-<br />';
// Максимальное количество символов, на которое будет разбиваться длинная строка
$divider['max']  = 20;

$str['out1'] = split_string($str['in']);
echo '<table border="1">';
foreach ($str as $key=>$val) {
  echo '<tr><td>'.$key.'</td><td>'.htmlentities($val,ENT_QUOTES,'cp1251').'</td></tr>';
}
echo '</table>';
Данный код делил текст длинее определенного размера по пробелу, т.е. анализировались только куски текста без пробелов. Нужна минимальная адаптация под конкретные нужды. С благодарностью приму конечный вариант.
 

msdn11

Новичок
$result = mysql_query("select LOCATE(' ', REVERSE(MID(text,$from,1000))) from raskaz where id = $ras");
$row = mysql_fetch_array($result);
$pos = $row[0];
$result = mysql_query("select MID(text,$from,1000-$pos) as text from raskaz where id = $ras");
//echo "select MID(text,$from,$to) as text from raskaz where id = $ras";
$row = mysql_fetch_array($result);
$from = $from + 1000 - $pos;

-~{}~ 09.12.05 15:06:

вот например так. вігребается из базы до последнего пробела в первой 1000 символов :)
 

voituk

прозревший
Это все конечно хорошо,
но проблему в "разрезанными" тэгами это не решает.
 

netmac

Новичок
Я бы делал регулярными выражениями.
Алгоритм примерно такой

1. забираем в $page текст до первого тэга
2. берем имя первого тэга (допустим будет div)
3. $page .= текст от первого открывающего div до первого закрывающего див. вложенные тэги не проверяем.
4. текст скопированный в $page отрезаем от анализируемого теста.
5. если strlen($page)>1000 выход. нет - переходим к шагу 2

писать лень. но надеюсь подход ясен.
резаных тегов не будет, но размер страницы может быть меньше 1000, со всеми вытекающими
 

Rurick

Новичок
А тебе обязательно эти тэги внутри нужны?
Иначе у тебя выйдет давольно навороченный анализатор. Т.к. вложеные тэги надо учитывать.
Например <div class = 'text0'>бла бла <div class='text1'> траливали </div> ...
В этом случае когда отрежет по алгоритму netmac'а без учёта вложенности, то выйдет билеберда. А если там таблицы какие или ещё что... ???
 

netmac

Новичок
Надо считать открывающие тэги с тем же именем (div) и искать соответствующий первому div закрывающий тэг.

Довольно сложно.
 

voituk

прозревший
В принципе проблемма должна решаться классически а-ля "Школьная задача про правильность расстановки скобок" на основе стека.

Но вот реализовать это дело для разбивки текста все руки не доходят... а надо бы
 

alexhemp

Новичок
voituk

HTML все-таки посложнее чем арифмитические выражения.

Нужно строить DOM-дерево и уже его делить согласно "весу" веток, а вес присваивать на основе каких-то эвристик.
 

Invizz

Новичок
расстановка скобок решается с переменной счетчиком.

Скобка открывается=счетчик+1
Скобка закрывается=счетчик-1

Таким образом пройтись по тексту. После примерно 900 символов начинать замерять. Если счетчик =0, то режем страницу.
 

voituk

прозревший
Автор оригинала: alexhemp
voituk

HTML все-таки посложнее чем арифмитические выражения.

Нужно строить DOM-дерево и уже его делить согласно "весу" веток, а вес присваивать на основе каких-то эвристик.
Че-то я не понимаю.

Как по мне достаточно разбить текст (можно регулярным выражением ) на узлы вида:

Код:
Привет, 
<b>
М
<BIG>
И
</BIG>
Р!
</b>
<br/>
У меня всё хорошо! На 
<a href="http://phpclub.ru">
phpClub-е
</a>
меня почти не обижают
потом свести их в такие:
Код:
Привет, 
<b>М<BIG>И</BIG>Р!</b>
<br/>
У меня всё хорошо! На 
<a href="http://phpclub.ru">phpClub-е</a>
меня почти не обижают
а потом уже оперировать не словами, а такими блоками.
Пока не перебрали количества символов в строке добавляем содердимое следующего блока.
Вот собссно и усе.

Пока правда реализовать не пробовал - не critical задача.
 

voituk

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

P.S. Вот только не могу понять где тут ресурсоемкость если ни одно строки кода не написал.
Или ты уже знаешь как я это буду реализовывать?
 

Invizz

Новичок
уже знаю, да.
метод не зависит от реализации, метод - метод, реализация - реализация.

Реализация может усугубить метод, а метод он какой есть, такой есть.

Ровно над твом постом мой пост, и гласит он:

Скобка открывается=счетчик+1
Скобка закрывается=счетчик-1

Таким образом пройтись по тексту. После примерно 900 символов начинать замерять. Если счетчик =0, то режем страницу.
для не особо проницательных, на пальцах:

С самого начала текста, по одной букве проверяем. Если тег открылся - $i++; если тег закрылся - $i--;

c примерно n-5% килобайт начинаем тормозить. Если $i==0, то брейкаем страницу.

---

Но есть одно но и относится оно ко всем методам. Это неправильная расстановка тегов.
 

voituk

прозревший
Invizz
Знаешь как я буду это писать? - А почему я ещё не знаю.
Напиши - интересно взглянуть.

Ты только что описал реализацию. А разглагольствовал про метод :)
Чем принципиально отличается то что предложил я от того что написал ты?

Какая (в терминах метода) разница буду я читать текст по буковке, или по xml-ноде.

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

-~{}~ 17.01.06 15:26:

Считаю данную дискуссию закрытой.
 

Invizz

Новичок
Считаешь? Я - нет.
Итак, метод же заключается в определении первого нулевого уровня вложенности, отсекая вложенные друг в друга.

Разница заключается в количестве действий и условий.

Никто тебя не учит, пиши как хочешь, просто есть быстрые и простые алгоритмы, а есть сложные и ресурсоемкие.
 

voituk

прозревший
Итак, метод же заключается в определении первого нулевого уровня вложенности, отсекая вложенные друг в друга.
и? Где я написал обратное?

Разница заключается в количестве действий и условий.
Сколько я привел условий?
 
Сверху