将纯文本呈现为 HTML 维护空白 - 没有 <pre>
Posted
技术标签:
【中文标题】将纯文本呈现为 HTML 维护空白 - 没有 <pre>【英文标题】:Rendering Plaintext as HTML maintaining whitespace – without <pre> 【发布时间】:2011-06-27 19:30:35 【问题描述】:给定任何包含可打印字符的任意文本文件,如何将其转换为呈现完全相同的 html(具有以下要求)?
除了默认的 HTML 空白规则外,不依赖任何其他规则 没有<pre>
标签
没有 CSS white-space
规则
<p>
标签很好,但不是必需的(<br />
s 和/或<div>
s 很好)
空白被完全保留。
鉴于以下输入行(忽略错误的自动语法突出显示):
Line one
Line two, indented four spaces
浏览器应该呈现完全相同的输出,保持第二行的缩进以及“缩进”和“空格”之间的间隙。当然,我实际上并不是在寻找等宽输出,而且字体与算法/标记正交。
鉴于这两行是一个完整的输入文件,正确的输出示例应该是:
Line one<br /> Line two,
indented four spaces
浏览器中的软包装是可取的。也就是说,生成的 HTML 不应强制用户滚动,即使输入行比其视口宽(假设单个单词仍比所述视口窄)。
我正在寻找完全定义的算法。在 python 或 javascript 中实施的奖励积分。
(请不要只回答我应该使用 <pre>
标签或 CSS white-space
规则,因为我的要求会使这些选项站不住脚。也请不要发布未经测试和/或幼稚的建议,例如就像“用&nbsp;
替换所有空格。”毕竟,我很肯定一个解决方案在技术上是可行的——这是一个有趣的问题,你不觉得吗?)
【问题讨论】:
“用 替换所有空格”有什么问题?只是问一下,这是首先想到的 - 当然,您还需要用<br>
替换换行符并以某种方式展开选项卡。
“这是一个有趣的问题,你不觉得吗?” - 并不真地。这个问题已经通过不止一种方式为您解决了。
@delnan,可能是因为这不允许浏览器换行
您只关心行首的空格,还是想保留行内文本的位置?或者,换句话说,您的文本中是否有要保留的列?
@matt “你的问题是不必要的。”你不知道。由于上面提到的限制,我上面提到的幼稚解决方案是站不住脚的。如果解决方案这么明显,请回答!
【参考方案1】:
使用zero-width space (&#8203;
) 保留空格并允许文本换行。基本思想是将每个空间或空间序列与零宽度空间配对。然后用不间断的空格替换每个空格。您还需要对 html 进行编码并添加换行符。
如果您不关心 unicode 字符,那是微不足道的。你可以使用string.replace()
:
function textToHTML(text)
return ((text || "") + "") // make sure it is a string;
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/\t/g, " ")
.replace(/ /g, "​ ​")
.replace(/\r\n|\r|\n/g, "<br />");
如果空格可以换行,则将每个空格与上面的零宽度空格配对。否则,要将空格保持在一起,请将每个 sequence 空格与零宽度空格配对:
.replace(/ /g, " ")
.replace(/(( )+)/g, "​$1​")
要编码 unicode 字符,它有点复杂。您需要迭代字符串:
var charEncodings =
"\t": " ",
" ": " ",
"&": "&",
"<": "<",
">": ">",
"\n": "<br />",
"\r": "<br />"
;
var space = /[\t ]/;
var noWidthSpace = "​";
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
没有等宽字体,您将丢失列:
十 七个空格 十一个 四个空格
看来要解决这个问题的算法会非常复杂。
【讨论】:
哇,谢谢。我很快就会试试这个。问题:您对“如果您不关心 Unicode”有何评论? (我通常会这样做。)等宽位当然是正确的,尽管我拥有的纯文本源实际上已经被假定为可以以可变宽度字体显示。 @Alan - 为了转义 unicode 字符,例如ñ
,您需要使用它们的字符代码(即,&#241;
)。从replace()
的调用中获取字符代码是不收费的,因此必须迭代字符串以逐个处理字符。
啊,谢谢你的澄清。不过,我不确定这些实体是否必要。假设一切都已经在 UTF-8 中,使用标头 Content-Type: text/html; charset=utf-8
就足够了。
似乎满足了我的所有需求! :) Test【参考方案2】:
虽然这并不能完全满足您的所有要求 - 一方面它不处理选项卡,但我使用了以下 gem,它在 javascript String
s 中添加了一个 wordWrap()
方法,在几个有机会做与您所描述的事情类似的事情——因此,想出一些可以做您想要做的额外事情的事情可能是一个很好的起点。
//+ 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");
;
【讨论】:
扩展一个本质上被混淆的函数有点棘手 @Alan H.:是的,而且也很难调试。我认为作者打算将它用于书签中——这是我使用它的地方——因此试图最小化它的长度(而不是让它难以理解)。扩展它是了解它为增强或其他修改做准备的好方法。【参考方案3】:如果您在项目中使用 jQuery 库,则非常简单。
只需一行,将asHTml
扩展名添加到String类和:
var plain='<a> i am text plain </a>'
plain.asHtml();
/* '<a> i am text plain </a>' */
演示:http://jsfiddle.net/abdennour/B6vGG/3/
注意:您不必访问 DoM。只需使用 jQuery
的构建器设计模式$('<tagName />')
【讨论】:
我认为这并不能解决手头的问题。您从一个 HTML 字符串开始,并且没有测试纯文本源中任意数量的空白的情况。【参考方案4】:在允许浏览器换行的同时做到这一点的解决方案是将两个空格的每个序列替换为一个空格和一个不间断空格。
浏览器将正确呈现所有空格(正常和非中断的),同时仍然换行(由于正常的空格)。
Javascript:
text = html_escape(text); // dummy function
text = text.replace(/\t/g, ' ')
.replace(/ /g, ' ')
.replace(/ /g, ' ') // second pass
// handles odd number of spaces, where we
// end up with " " + " " + " "
.replace(/\r\n|\n|\r/g, '<br />');
【讨论】:
虽然您的代码看起来很像您复制并调整了我的代码,但您已经处理了软包装和 HTML 转义问题,而我没有,并且重复的答案没有意义,所以我会删除我的并支持你的。 我在你的回答之后添加了标签部分,因为我忘记了它。现在这是很常见的代码 :-) 支持您的评论作为回报。 这真的不是问题,而且替换的东西无论如何都是微不足道的。我已经多次从其他答案中获取线索。 这感觉比我想象的要近得多,但空间的处理实际上并不完全正确。见this test page。突出显示空格,您会发现它们不太正确。 很好,我稍微调整了一下,以尽可能避免以&nbsp;
开头的行,它似乎确实有效! jsfiddle.net/nayyP/3以上是关于将纯文本呈现为 HTML 维护空白 - 没有 <pre>的主要内容,如果未能解决你的问题,请参考以下文章