【问题标题】:Javascript Regular Expression to Parse HTML and word wrap?用于解析 HTML 和自动换行的 Javascript 正则表达式?
【发布时间】:2011-12-28 00:51:18
【问题描述】:

我需要创建一些 Javascript,它可以从文本框中搜索输入的 HTML,并忽略所有标签以自动自动换行,例如 70,并添加 <br> 标签。

我还需要找到所有像 ©– 这样的 ascii 并将其计为一个空格而不是 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 是不可能使用的。

【问题讨论】:

  • 为什么不能使用 CSS?
  • 使用正则表达式匹配 HTML 在我看来从来没有是个好主意,那么 DOM 遍历呢?没办法?
  • 那个数字代表 HTML 源代码中的字符数或输出文本(将是解析 HTML 的结果的文本)中的字符数?
  • CSS 是不可能的,因为我在一家非常大的公司工作,我们在那里做电子邮件。不幸的是,多个电子邮件平台在 CSS 上表现不佳。
  • stackoverflow.com/a/1732454/615754 你想假设&amp;anytext; 是一个html 实体并将它算作一个字符,还是你想让你的代码实际检查所有有效的html 实体?跨度>

标签: javascript regex html-parsing word-wrap


【解决方案1】:

虽然“正则表达式”和“解析 HTML”这两个短语的组合通常会导致 entire universes to crumble,但您的用例似乎足够简单以至于它可以工作,但是您希望在包装后保留 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 标记(bemstrong)被保留,只是 Markdown 没有显示它们。

基本上,输入字符串在每个空格处被拆分为单词,这很幼稚,可能会造成麻烦,但这是一个开始。然后,在删除任何类似于 HTML 标记或实体的内容后,计算每个单词的长度。然后就是遍历每个单词,保持我们所在列的运行记录;一旦我们达到 70,我们将聚合的单词弹出到输出字符串中并重置。同样,它非常粗糙,但对于大多数基本 HTML 来说已经足够了。

【讨论】:

  • +1。我喜欢。 (虽然我有点困惑,为什么您不能在示例输出中显示 html 标记。)
  • 好吧,我想要一个等宽字体来证明所有内容都被正确包装,而块引用——同时允许粗体和斜体文本——却没有做到这一点。唯一的其他选择是将其标记为代码输出,但会打印原始星号和下划线。有什么方法可以将我没有看到的代码加粗和斜体?
  • 很公平。不,您不能将已经格式化为代码的文本加粗(至少,我不知道如何)。
  • 不处理:&lt;b title="tags having attributes or spaces"&gt;
【解决方案2】:

此解决方案通过将令牌计数到所需的行长度来“遍历”字符串令牌。正则表达式捕获四种不同标记之一:

  • $1:HTML 打开/关闭标签(宽度 = 0)
  • $2:HTML 实体。 (宽度 = 1)
  • $3:行终止符。 (计数器已重置)
  • $4:任何其他字符。 (宽度 = 1)

请注意,我添加了一个行终止符标记,以防您的文本框已经用换行符格式化(带有可选的回车符)。这是一个 JavaScript 函数,它使用 String.replace() 和一个匿名回调来计算令牌:

function 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)

【讨论】:

    【解决方案3】:

    不想释放Cthulhu,我决定(与我的其他答案不同)改为提供一个不尝试使用正则表达式解析 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']);
    
    });
    

    注意递归 - 不能用正则表达式做到这一点!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-03-14
      • 2015-05-30
      • 1970-01-01
      • 2014-05-16
      • 2011-06-11
      • 2010-09-08
      • 1970-01-01
      相关资源
      最近更新 更多