web-dev-qa-db-ja.com

混合テキストでイタリックを反転させる正しいアルゴリズムは何ですか?

質問の動機は、以下のセクションに描かれています。 テキストをイタリックにする多くの方法 があるため、おそらく1つ以上の良い "swap イタリックアルゴリズム "。この問題は、XHTMLコードにいくつかの追加の難しさを示しており、_<i>_タグを使用していますバランスを取る必要があります。例:

_ <!-- original text: -->
 <p id="p1"><i>Several more</i> Homo sapiens <i>fossils were discovered</i>.</p>
 <!-- same text, swapping italics: -->
 <p id="p2">Several more <i>Homo sapiens</i> fossils were discovered.</p>
_

このように見えます

  1. さらに数個ヒト化石が発見されました

  2. さらにいくつかのホモサピエンスの化石が発見されました。


アルゴリズムの紹介と議論

"layout solution"の場合、最も簡単なアルゴリズムは、すべてのテキストブロックの_font-style_ CSSプロパティをチェックし、jQueryで反転します。

_$('#myFragment *').each(function(){
   if ($(this).css('font-style')=='italic')
       $(this).css('font-style','normal')
    else
       $(this).css('font-style','italic')        
}); 
_

しかし、このアルゴリズムはもう少し複雑なテストに耐えられません。

_ <p id="p3"><b><i>F</i>RAGMENT <big><i>with italics</i> and </big> withOUT.</b></p>
_

2番目に単純なアルゴリズムはコンクリートソリューション用であり、「例」セクションで使用されました。 2つのステップがあります。

  1. xHTMLフラグメントをイタリック体で囲みます。
  2. イタリックタグの開閉を反転します(例:_</i>_から_<i>_)。

つまり、Javascriptで書く、

_var s = '... a fragment of XHTML content ...';
s = '<i>'+
   s.replace(/<(\/?)i>/mg, 
          function (m,p1){
              return p1? '<i>': '</i>';
          }
   ) +
   '</i>';  
_

しかし、タグのバランスを失って、2番目のテストにも耐えられません...「修正された」アルゴリズムは実行されますが(!)、移植性がなく、高速でもエレガントでもありません。それは ここに示されています であり、以下の例のセクションにあります。

ポイント!

だから問題は、

シンプルで優れた汎用的な(どのブラウザでも使用可能で、他の言語に移植できる)アルゴリズムはありますか?別の「スワップイタリックアルゴリズム」を知っていますか?

PS:私があなたのアルゴリズムをXSLTに変換するという意味での「一般的な」。アルゴリズム直接生成する必要があります バランスのとれたXHTMLコード(Tidyのような中間ブラックボックスなし)。


動機

「スワップイタリックアルゴリズム」をテキストエディター、サーバーパーサーなどに移植する必要があります。すべてのケースで、標準のXHTMLおよび_<i>_タグによって「入力(および出力)を正規化」できます。

私は、さまざまな起源とスタイルからエクスポートされた散文の本と科学記事のXHTMLテキストを解析しています...ほとんどのテキストは「通常のテキスト」としてエクスポートされますが、多くのタイトル(例:記事のタイトル、章のタイトル)、そして時には、完全な章または完全なテキストボックス(記事の要約など)は、イタリック体で様式化されています。これらすべての「イタリックで様式化された」は、逆にする必要があります。典型的なケース:

  • 元の「すべての章のイタリック」を「すべての章の通常のテキスト」に変換します: この場合を参照 。約300ページの書籍では、25の章のうち8つの章を反転する必要があります。

  • イタリック体の引用符、要約など。 この例 を参照してください。通常に戻す必要がありますが、強調の言葉を失うことはありません。

  • 科学的テキストでの 種の二項名 の記述は、通常、イタリック体でタイプセットされます(または「通常のテキスト」で使用されるものとは異なるフォントで反転されます)。 XHTMLでエクスポートされた記事の(記事および記事セクションの)斜体の何百ものタイトルを職場で反転させる必要があります。 PS:質問の最初の例を参照してください(「さらにいくつかHomo sapiens...」)。

「タグバランス修正」が存在しない(答えの)汎用アルゴリズムをXSLTライブラリに変換するも必要です。

JavaScriptおよびPHP a non-generic "イタリックアルゴリズムをスワップする"で実装します。一般的なものには、一般的な "XMLインターリーブアルゴリズム"が必要です...ここでは、ブラウザの( DOM)とTidy修正、「インターリーブ」の代替手段として。

Javascript

複雑な入力(!)で実行されます。説明 jQuery実装

_ var s = $('#sample1').html(); // get original html text fragment

 // INVERSION ALGORITHM: add and remove italics.
 s = "<i>"+
     s.replace(/<(\/?)i>/mg, 
               function (m,p1){
                   return p1? '<i>': '</i>';
               }
     ) +
     "</i>";  // a not-well-formed-XHTML, but it is ok...
 $('#inverted').html(s); // ...the DOM do all rigth!
 // minor corrections, for clean empties:
 s = $('#inverted').html();
 s = s.replace(/<([a-z]+)>(\s*)<\/\1>/mg,'$2'); // clean
 s = s.replace(/<([a-z]+)>(\s*)<\/\1>/mg,'$2'); // clean remain
 $('#inverted').html(s);  
 // END ALGORITHM

 alert(s);
_

PHP、Tidy

PHP=に「翻訳」されたJavascriptと同じ—自然な翻訳はDOMDocument()クラスとloadHTML/saveXMLメソッドを使用していますが、ブラウザのコレスポンデントと同じ動作をするのは tidy class です。同じ結果を表示します(!)

_ $sample1='<b><i>O</i>RIGINAL <big><i>with italics</i> and </big> withOUT</b>';
 $inverted = '... inverted will be here ...';
 echo $sample1;
 // Tidy correction
 $s = $sample1; // get original html text fragment
  // INVERSION ALGORITHM: add and remove italics.
  $s = "<i>".
      preg_replace_callback('/<(\/?)i>/s', function ($m){
       return $m[1]? '<i>': '</i>';}, $s) .
      "</i>";  // a not-well-formed-XHTML, but it is ok...
  $config = array('show-body-only'=>true,'output-xhtml'=>true);
  $tidy = new tidy;
  $tidy->parseString($s, $config, 'utf8');
  $s = $tidy;  // ... because Tidy corrects!     
  // minor corrections, for clean empties:
  $s = preg_replace('/<([a-z]+)>(\s*)<\/\1>/s', '$2', $s); // clean
  $s = preg_replace('/<([a-z]+)>(\s*)<\/\1>/s', '$2', $s); // clean remain
  // END ALGORITHM
  echo "\n\n$s";
_
8
Peter Krauss

更新(2013年6月18日):この回答を使用してアルゴリズムを説明し、結論を要約します。


JQueryトラバースと「レイアウトソリューション」の回避策について。

@Wilbertのコメントの後、check .prop()の反復により変化する.each()の動的動作を回避するために、「最も単純なアルゴリズム」を採用し、elseを削除しました。すべての反復の後、「親イタライザ」が解決します。 ここを参照 または以下のコード。

_$('#myFragment *').each(function(){
   if ($(this).css('font-style')=='italic')
       $(this).css('font-style','normal');
});
$('#myFragment').parent().css('font-style','italic');
_

動的な振る舞いを処理する別の方法は、prop('tagName')を使用して、変更されない静的プロパティをチェックすることです。 ここを参照 または以下のコード。

_$('#myFragment').parent().css('font-style','italic');
$('#myFragment *').each(function(){
   if ($(this).prop('tagName')=='I')  // not changes with parent
       $(this).css('font-style','normal');
});
_

より多くのテストが必要で、スタイルプロパティを具体的な_<i>_タグに変更するには、最終的な解析が必要です。アルゴリズムを2回適用するには、注意が必要です。


レイアウトソリューション

これは現在の質問の解決策ではありませんが、いくつかの良い手がかりを生み出し、は「レイアウトの問題」の最良の(少なくとも最小!)解決策です!

toggleClass() method を使用して、「イタリック体のクラス」から「通常のテキストクラス」にswapできます。 ここを参照 または以下のコード。

_ $('#myFragment *').each(function(){
     $(this).toggleClass( "original change");
 });
_

そして、この小さなアルゴリズムを2回、そして必要なだけ何度でも適用できます...これは良い解決策です!しかし、これは「rewrite XMLアルゴリズム」ではなく、ここでCSSが鍵となります

_ .original { font-style:normal; } /* use class="original" in your XHTML fragment */
i.original { font-style:italic; }

 .change { font-style:italic; }
i.change{ font-style:normal; }
_

...したがって、_<i>_タグを変換するアルゴリズムの場合、問題は未解決のままです...

具体的な解決策

@ DanielHaleyの修正 に基づく「純粋なXSLT1での100%ソリューション」(多くのケースでテストされています!)これは、効果的な_<i>_タグ変換です。

_ <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:preserve-space elements="p"/>

<xsl:template match="@*|node()"> <!-- copy all -->
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="i"> <!-- remove tag i -->
    <xsl:apply-templates/>
</xsl:template>

<xsl:template match="text()[not(ancestor::i)]"> <!-- inlcude tag i -->
    <i><xsl:copy-of select="."/></i>
</xsl:template>
 </xsl:stylesheet>
_

コピープロセスの「ドライブバイイベントアルゴリズム」としての概要:

  • iタグを削除:「_<i>_thing_</i>_」のすべてのものを「thing "。

  • iタグを含める:任意のtextを「_<i>_text_</i>_」としてコピーします、textがイタリックの親のコンテキストに含まれていない場合。 PS:textはDOMツリーのターミナルノードです。

結論

「レイアウトソリューション」は テキストエディター に適していますが、いくつかのトリックと厳密でないソリューションを使用しています(オーバーラップ、パフォーマンスなどは関係ありません)。 XMLプロセスでは、_<i>_タグの変換を処理する必要があります...したがって、アルゴリズムを表現するための自然言語はXSLTまたはxQueryです。

XSLTで実装されたアルゴリズムは、フレームワークの必要性を示しています。

  1. ancestor(親、祖父母など)セレクタ。「イタリックコンテキスト」かどうかを確認します。

  2. テキストノード(DOM text())アクセス。

  3. iタグを削除して含めます。

したがって、各フレームワークの問題を確認できます。

  • [〜#〜] dom [〜#〜] (W3C標準フレームワーク):項目3のDOMDocument::renameNode()はまだ実装されていません(PHP、Javascriptなどを参照)。 )。

  • jQuery:項目2には便利な関数がありません この答え を参照してください。

  • XSLT:アルゴリズムを表現するのに最適ですが、JavaScriptなどのコンテキストでは使用できません。

私(またはあなたが言いました!)は、「純粋なDOM2」メソッドでXSLTアルゴリズムを表現しようとします。そのDOMバージョンは「一般的なアルゴリズム」になります...さて:変換がDOM3に対してのみ有効である場合(renameNodeやその他のトリックを使用)、現時点での結論は「一般的な/翻訳可能なアルゴリズムは存在しない」ということです。

2
Peter Krauss

https://stackoverflow.com/a/17156452/317052 からのXSLT試行...

これがすべてのケースをカバーするかどうかはわかりませんが、これを行うことができます:

XML入力

<html>
    <!-- original text: -->
    <p id="p1"><i>Several more</i> Homo sapiens <i>fossils were discovered</i>.</p>
    <!-- same text, swapping italics: -->
    <p id="p2">Several more <i>Homo sapiens</i> fossils were discovered.</p>
    <p>Leave me alone!</p>
    <p><b><i>O</i>RIGINAL <big><i>with italics</i> and </big> withOUT</b></p>
</html>

XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="*[i]">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:apply-templates select="node()" mode="swapItal"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="text()" mode="swapItal" priority="1">
        <i><xsl:value-of select="."/></i>
    </xsl:template>

    <xsl:template match="i" mode="swapItal">
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="@*|node()" mode="swapItal">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" mode="swapItal"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

XML出力

<html>
   <!-- original text: -->
   <p id="p1">Several more<i> Homo sapiens </i>fossils were discovered<i>.</i></p>
   <!-- same text, swapping italics: -->
   <p id="p2"><i>Several more </i>Homo sapiens<i> fossils were discovered.</i></p>
   <p>Leave me alone!</p>
   <p><b>O<i>RIGINAL </i><big>with italics<i> and </i></big><i> withOUT</i></b></p>
</html>

レンダリングされた入力

さらにいくつか ホモサピエンス 化石が発見された

ホモサピエンス

ほっといて!

O元の 斜体 そしてwithOUT


レンダリングされた出力

さらにいくつか ホモサピエンス 化石が発見された

さらにいくつか化石が発見されました。

ほっといて!

O元の 斜体 そしてwithOUT

1
Daniel Haley