10

给定任何充满可打印字符的任意文本文件,如何将其转换为呈现完全相同的 HTML(具有以下要求)?

  • 除了默认的 HTML 空白规则之外,不依赖任何其他规则
    • <pre>标签
    • 没有 CSSwhite-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 不应强制用户滚动,即使输入行比其视口宽(假设单个单词仍比所述视口窄)。

我正在寻找完全定义的算法。在pythonjavascript中实现的奖励积分。

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

4

4 回答 4

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 />');
于 2011-02-15T18:09:32.690 回答
11

使用零宽度空格( &#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

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

 十七个空格
 十一四个空格

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

于 2011-02-18T23:42:04.837 回答
2

虽然这并不能完全满足您的所有要求 - 一方面它不处理选项卡,但我使用了以下 gem,它wordWrap()在 Javascript 中添加了一个方法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 美化器提供:

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");
};
于 2011-02-15T18:43:52.693 回答
0

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

只需一行,将扩展名添加asHTml到字符串类和:

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

演示:http: //jsfiddle.net/abdennour/B6vGG/3/

注意:您不必访问 DoM。只需使用 jQuery 的构建器设计模式 $('<tagName />')

于 2014-01-24T06:28:45.230 回答