10

印刷可能な文字でいっぱいの任意のテキスト ファイルが与えられた場合、これをまったく同じようにレンダリングされる HTML に変換するにはどうすればよいでしょうか (次の要件があります)。

  • デフォルトの HTML 空白規則以外には依存しません
    • <pre>タグなし
    • CSSwhite-spaceルールなし
  • <p>タグは問題ありませんが、必須ではありません (<br />および/または<div>は問題ありません)
  • 空白は正確に維持されます。

    次の入力行があるとします (誤った自動構文強調表示は無視してください)。

    Line one
        Line two, indented    four spaces
    

    ブラウザーは、2 行目のインデントと「インデント」と「スペース」の間のギャップを維持して、出力をまったく同じにレンダリングする必要があります。もちろん、私は実際に等幅の出力を探しているわけではなく、フォントはアルゴリズム/マークアップに直交しています。

    完全な入力ファイルとして 2 行を指定すると、正しい出力の例は次のようになります。

    Line one<br />&nbsp;&nbsp;&nbsp;&nbsp;Line two, 
    indented&nbsp;&nbsp;&nbsp; four spaces
    
  • ブラウザでのソフト ラッピングが望ましいです。つまり、結果の HTML は、入力行がビューポートよりも広い場合でも、ユーザーにスクロールを強制するべきではありません (個々の単語がまだビューポートよりも狭いと仮定します)。

完全に定義されたアルゴリズムを探しています。pythonまたはjavascriptでの実装のボーナス ポイント。

<pre>(タグや CSSルールを使用する必要があるとだけ答えないでwhite-spaceください。私の要件ではこれらのオプションを受け入れられないからです。また、「すべてのスペースを に置き換える」など、テストされていない、または単純な提案を投稿しないでください&nbsp;。結局のところ、私は解決策が技術的に可能であることは間違いありません — 興味深い問題だと思いませんか?)

4

4 に答える 4

14

ブラウザが長い行を折り返すことを許可しながらそれを行うための解決策は、2つのスペースの各シーケンスをスペースと非ブレークスペースに置き換えることです。

ブラウザは、(通常のスペースのために)長い行を折り返しながら、すべてのスペース(通常のスペースと切れ目のないスペース)を正しくレンダリングします。

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

等幅フォントがないと、列が失われます。

 10 7 スペース
 11 4 スペース

それを修正するアルゴリズムは非常に複雑になるようです。

于 2011-02-18T23:42:04.837 に答える
2

これはすべての要件を完全に満たすわけではありませんが、タブを処理しないことが 1 つありますが、wordWrap()Javascript にメソッドを追加する次の gemStringを数回使用して、あなたがしたことと同様のことを行いました。再記述 — したがって、あなたが望む追加のことも行う何かを考え出すことは良い出発点かもしれません.

//+ 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ライブラリを使用する場合、これは非常に簡単です。

1行だけ、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 に答える