将纯文本呈现为HTML维护空白 - 没有<pre></pre>

时间:2011-02-15 18:05:09

标签: javascript python html algorithm plaintext

如果任意文本文件中包含任何可打印字符,那么如何将其转换为完全相同的HTML(具有以下要求)?

  • 不依赖于默认的HTML空格规则
    • <pre>代码
    • 没有CSS white-space规则
  • <p>代码很好,但不是必需的(<br /> s和/或<div> s很好)
  • 准确维护空白。

    给出以下输入行(忽略错误的自动语法高亮显示):

    Line one
        Line two, indented    four spaces
    

    浏览器应该使输出完全相同,保持第二行的缩进以及“缩进”和“空格”之间的间隙。当然,我实际上并不是在寻找等宽输出,而且字体与算法/标记正交。

    将两行作为完整的输入文件,示例正确的输出将是:

    Line one<br />&nbsp;&nbsp;&nbsp;&nbsp;Line two, 
    indented&nbsp;&nbsp;&nbsp; four spaces
    
  • 浏览器中的软包装是可取的。也就是说,生成的HTML不应强制用户滚动,即使输入行比其视口宽(假设单个单词仍然比所述视口缩小)。

我正在寻找完全定义的算法。 python javascript 中实施的加分点。

(请不要回答我应该使用<pre>标签或CSS white-space规则,因为我的要求会使这些选项无法维持。请不要发布未经测试的和/或天真的建议,例如“用&nbsp;替换所有空格。”毕竟,我很肯定解决方案在技术上是可行的 - 这是一个有趣的问题,你不觉得吗?)

4 个答案:

答案 0 :(得分:14)

在允许浏览器换行长行的同时做到这一点的解决方案是用空格和非中断空格替换两个空格的每个序列。

浏览器将正确渲染所有空格(正常和非破坏),同时仍然包裹长行(由于正常空格)。

使用Javascript:

text = html_escape(text); // dummy function
text = text.replace(/\t/g, '    ')
           .replace(/  /g, '&nbsp; ')
           .replace(/  /g, ' &nbsp;') // second pass
                                      // handles odd number of spaces, where we 
                                      // end up with "&nbsp;" + " " + " "
           .replace(/\r\n|\n|\r/g, '<br />');

答案 1 :(得分:10)

使用zero-width space&#8203;)保留空格并允许文本换行。基本思想是将每个空间或空间序列与零宽度空间配对。然后用不间断的空间替换每个空间。您还需要对html进行编码并添加换行符。

如果你不关心unicode字符,那就太微不足道了。您可以使用string.replace()

function textToHTML(text)
{
    return ((text || "") + "")  // make sure it is a string;
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/\t/g, "    ")
        .replace(/ /g, "&#8203;&nbsp;&#8203;")
        .replace(/\r\n|\r|\n/g, "<br />");
}

如果白色空间可以换行,请将每个空间与零宽度空间配对,如上所述。否则,为了将空白区域保持在一起,将空间的每个序列与零宽度空间配对:

    .replace(/ /g, "&nbsp;")
    .replace(/((&nbsp;)+)/g, "&#8203;$1&#8203;")

要对unicode字符进行编码,它会更复杂一些。您需要迭代字符串:

var charEncodings = {
    "\t": "&nbsp;&nbsp;&nbsp;&nbsp;",
    " ": "&nbsp;",
    "&": "&amp;",
    "<": "&lt;",
    ">": "&gt;",
    "\n": "<br />",
    "\r": "<br />"
};
var space = /[\t ]/;
var noWidthSpace = "&#8203;";
function textToHTML(text)
{
    text = (text || "") + "";  // make sure it is a string;
    text = text.replace(/\r\n/g, "\n");  // avoid adding two <br /> tags
    var html = "";
    var lastChar = "";
    for (var i in text)
    {
        var char = text[i];
        var charCode = text.charCodeAt(i);
        if (space.test(char) && !space.test(lastChar) && space.test(text[i + 1] || ""))
        {
            html += noWidthSpace;
        }
        html += char in charEncodings ? charEncodings[char] :
        charCode > 127 ? "&#" + charCode + ";" : char;
        lastChar = char;
    }
    return html;
}  

现在,只是评论。如果不使用等宽字体,您将失去一些格式。考虑这些带有等宽字体的文本行如何形成列:

ten       seven spaces
eleven    four spaces

如果没有等宽字体,您将丢失列:

十七个空格
十一四个空间

似乎解决这个问题的算法非常复杂。

答案 2 :(得分:2)

虽然这不能完全满足您的所有要求 - 但有一件事它不处理标签,我使用了以下gem,它为Javascript wordWrap()添加了String方法,在某些情况下做一些类似于你所描述的事情 - 所以这可能是一个很好的起点,可以提出一些能够做你想要的其他事情的东西。

//+ Jonas Raoni Soares Silva
//@ http://jsfromhell.com/string/wordwrap [rev. #2]

// String.wordWrap(maxLength: Integer,
//                 [breakWith: String = "\n"],
//                 [cutType: Integer = 0]): String
//
//   Returns an string with the extra characters/words "broken".
//
//     maxLength  maximum amount of characters per line
//     breakWith  string that will be added whenever one is needed to
//                break the line
//     cutType    0 = words longer than "maxLength" will not be broken
//                1 = words will be broken when needed
//                2 = any word that trespasses the limit will be broken

String.prototype.wordWrap = function(m, b, c){
    var i, j, l, s, r;
    if(m < 1)
        return this;
    for(i = -1, l = (r = this.split("\n")).length; ++i < l; r[i] += s)
        for(s = r[i], r[i] = ""; s.length > m; r[i] += s.slice(0, j) + ((s = s.slice(j)).length ? b : ""))
            j = c == 2 || (j = s.slice(0, m + 1).match(/\S*(\s)?$/))[1] ? m : j.input.length - j[0].length
            || c == 1 && m || j.input.length + (j = s.slice(m).match(/^\S*/)).input.length;
    return r.join("\n");
};

我还想评论一下,在我看来,一般情况下,如果涉及标签,你会想要使用等宽字体,因为字的宽度会随着使用的比例字体而变化(产生结果)使用制表符非常依赖字体)。

更新:这是一个更具可读性的版本,由在线javascript beautifier提供:

String.prototype.wordWrap = function(m, b, c) {
    var i, j, l, s, r;
    if (m < 1)
        return this;
    for (i = -1, l = (r = this.split("\n")).length; ++i < l; r[i] += s)
        for (s = r[i], r[i] = ""; s.length > m; r[i] += s.slice(0, j) + ((s =
                s.slice(j)).length ? b : ""))
            j = c == 2 || (j = s.slice(0, m + 1).match(/\S*(\s)?$/))[1] ? m :
            j.input.length - j[0].length || c == 1 && m || j.input.length +
            (j = s.slice(m).match(/^\S*/)).input.length;
    return r.join("\n");
};

答案 3 :(得分:0)

如果在项目中使用jQuery库,则非常简单。

只需一行,将asHTml扩展名添加到String Class并:

var plain='&lt;a&gt; i am text plain &lt;/a&gt;'
plain.asHtml();
/* '<a> i am text plain </a>' */

DEMO:http://jsfiddle.net/abdennour/B6vGG/3/

  

注意:您无需访问DoM。只需使用jQuery的$('<tagName />')

的构建器设计模式