Регулярное выражение Javascript для разбора HTML и перенос слов? - PullRequest
0 голосов
/ 28 декабря 2011

Мне нужно создать немного Javascript, который может искать введенный HTML из текстового поля и игнорировать все теги, чтобы автоматически переносить слова по заданному номеру, например, скажем, 70, и добавить тег <br>.

Мне также нужно найти все ascii, такие как &copy; и &#150;, и посчитать это как один пробел, а не 5 или 4 пробела.

Таким образом, код будет принимать:

<b>Hello</b> Here is some code that I would like to wrap. Lets pretend this goes on for over 70 spaces.

Вывод будет:

<b>Hello</b> Here is some code that I would like to wrap. Lets pretend <br>
this goes on for over 70 spaces.

Возможно ли это? Как бы я начал? Уже есть инструмент для этого?

Кстати, о CSS не может быть и речи.

Ответы [ 3 ]

2 голосов
/ 28 декабря 2011

Хотя сочетание фраз "регулярное выражение" и "синтаксический анализ HTML" обычно приводит к разрушению целых вселенных , ваш вариант использования выглядит достаточно упрощенным, чтобы он мог работать, но тот факт, что вы хотите сохранить Форматирование HTML после переноса упрощает работу с последовательностью, разделенной пробелами. Вот очень грубое приближение того, что вы хотели бы сделать:

input = "<b>Hello</b> Here is some code that I would like to wrap. Let's pretend this goes on for over 70 spaces. Better &yen;&euro;&#177;, let's <em>make</em> it go on for more than 70, and pick &uuml;&thorn; a whole <strong>bu&ntilde;&copy;h</strong> of crazy symbols along the way.";
words = input.split(' ');

lengths = [];
for (var i = 0; i < words.length; i++)
  lengths.push(words[i].replace(/<.+>/g, '').replace(/&.+;/g, ' ').length);

line = [], offset = 0, output = [];
for (var i = 0; i < words.length; i ++) {
  if (offset + (lengths[i] + line.length - 1) < 70) {
    line.push(words[i]);
    offset += lengths[i];
  }
  else {
    output.push(line.join(' '));
    offset = 0; line = [], i -= 1;;
  }
  if (i == words.length - 1)
    output.push(line.join(' '));
}

output = output.join('<br />');

, что приводит к

Hello Here is some code that I would like to wrap. Let's pretend this
goes on for over 70 spaces. Better ¥€±, let's make it go on for more
than 70, and pick üþ a whole buñ©h of crazy symbols along the way.

Обратите внимание, что теги HTML (b, em, strong) сохраняются, просто уценка не показывает их.

По сути, входная строка разбивается на слова в каждом пробеле, что наивно и может вызвать проблемы, но это начало. Затем длина каждого слова вычисляется после удаления чего-либо, напоминающего HTML-тег или объект. Тогда нужно просто перебирать каждое слово, вести подсчет столбца, в котором мы находимся; Как только мы достигли 70, мы вставляем агрегированные слова в выходную строку и сбрасываем. Опять же, это очень грубо, но этого должно хватить для большинства базовых HTML.

0 голосов
/ 28 декабря 2011

Не желая раскрывать Ктулху , я решил (в отличие от моих собратьев ответы) вместо этого предоставить ответ на вашу проблему, который не пытается анализировать HTML с регулярными выражениями. Вместо этого я обратился к впечатляющей силе добра - jQuery - и использовал ее для анализа вашего HTML на стороне клиента.

Рабочая скрипка: http://jsfiddle.net/CKQ9f/6/

HTML:

<div id="wordwrapOriginal">Here is some code that I would like to wrap. Lets pretend this goes on for over 70 spaces.etend this g<b class="foo bar">Helloend this goes on for over 70 spaces.etend</b>Here is some code that I would like to wrap. Lets pretend this goes on for over 70 spaces.etend this g</div>
<hr>
<div id="wordwrapResult"></div>

JQuery:

// lifted from here: https://stackoverflow.com/a/5259788/808921
$.fn.outerHTML = function() {
    $t = $(this);
    if( "outerHTML" in $t[0] )
    { return $t[0].outerHTML; }
    else
    {
        var content = $t.wrap('<div></div>').parent().html();
        $t.unwrap();
        return content;
    }
}

// takes plain strings (no markup) and adds <br> to 
// them when each "line" has exceeded the maxLineLen
function breakLines(text, maxLineLen, startOffset)
{
   var returnVals = {'text' : text, finalOffset : startOffset + text.length};
   if (text.length + startOffset > maxLineLen)
   {
      var wrappedWords = "";
      var wordsArr = text.split(' ');
      var lineLen = startOffset;
      for (var i = 0; i < wordsArr.length; i++)
      {
        if (wordsArr[i].length + lineLen > maxLineLen)
        {
          wrappedWords += '<br>';
          lineLen = 0;
        } 
        wrappedWords += (wordsArr[i] + ' ');
        lineLen += (wordsArr[i].length + 1);
      } 
      returnVals['text'] = wrappedWords.replace(/\s$/, '');
      returnVals['finalOffset'] = lineLen;
   }
   return returnVals;
}

// recursive function which will traverse the "tree" of HTML 
// elements under the baseElem, until it finds plain text; at which 
// point, it will use the above function to add newlines to that text
function wrapHTML(baseElem, maxLineLen, startOffset)
{
    var returnString = "";
    var currentOffset = startOffset;

    $(baseElem).contents().each(function () {
        if (! $(this).contents().length) // plain text
        {
            var tmp = breakLines($(this).text(), maxLineLen, currentOffset);
            returnString += tmp['text'];
            currentOffset = tmp['finalOffset'];

        }
        else // markup
        {
            var markup = $(this).clone();
            var tmp = wrapHTML(this, maxLineLen, currentOffset);
            markup.html(tmp['html']);
            returnString += $(markup).outerHTML();
            currentOffset = tmp['finalOffset'];
        }
    });

    return {'html': returnString, 'finalOffset': currentOffset};
}


$(function () {

   wrappedHTML = wrapHTML("#wordwrapOriginal", 70, 0);

   $("#wordwrapResult").html(wrappedHTML['html']);

});

Обратите внимание на рекурсию - не можете сделать это с помощью регулярного выражения!

0 голосов
/ 28 декабря 2011

Это решение «обходит» строковый токен, считая токен до нужной длины строки.Регулярное выражение захватывает один из четырех разных токенов:

  • $ 1: HTML-тег открытия / закрытия (ширина = 0)
  • $ 2: HTML-сущность.(ширина = 1)
  • $ 3: терминатор строки.(счетчик сбрасывается)
  • $ 4: любой другой символ.(ширина = 1)

Обратите внимание, что я добавил токен-терминатор строки, если ваше текстовое поле уже отформатировано с переводом строки (с дополнительными возвратами каретки).Вот функция JavaScript, которая просматривает строку, используя String.replace(), и анонимный обратный вызов, подсчитывающий токены по ходу:

функция breakupHTML (text, len);

// Break up textarea into lines having len chars.
function breakupHTML(text, len) {
    var re = /(<(?:[^'"<>]+|'[^']*'|"[^"]*")*>)|(&(?:\w+|#x[\da-f]+|#\d+);)|(\r?\n)|(.)/ig;
    var count = 0;  // Initialize line char count.
    return text.replace(re,
        function(m0, m1, m2, m3, m4) {
            // Case 1: An HTML tag. Do not add to count.
            if (m1) return m1;
            // Case 2: An HTML entity. Add one to count.
            if (m2) {
                if (++count >= len) {
                    count = 0;
                    m2 += '<br>\n';
                }
                return m2;
            }
            // Case 3: A hard coded line terminator.
            if (m3) {
                count = 0;
                return '<br>\n';
            }
            // Case 4: Any other single character.
            if (m4) {
                if (++count >= len) {
                    count = 0;
                    m4 += '<br>\n';
                }
                return m4;
            } // Never get here.
        });
}

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

p = re.compile(r"""
    # Match one HTML open/close tag, HTML entity or other char.
      (<(?:[^'"<>]+|'[^']*'|"[^"]*")*>)  # $1: HTML open/close tag
    | (&(?:\w+|\#x[\da-f]+|\#\d+);)      # $2: HTML entity.
    | (\r?\n)                            # $3: Line terminator.
    | (.)                                # $4: Any other character.
    """, re.IGNORECASE | re.VERBOSE)
...