Was ist der richtige Algorithmus, um Kursivschrift in einem gemischten Text zu invertieren?

8

Die Motivationen der Frage wurden im folgenden Abschnitt dargestellt. Es gibt viele Möglichkeiten, Text kursiv zu machen , daher gibt es möglicherweise mehr als einen guten " Swap- Kursiv-Algorithmus ". Das Problem zeigt einige zusätzliche Schwierigkeiten in einem XHTML-Code und die Verwendung des <i>Tags, die ausgeglichen werden müssen . Beispiel:

 <!-- 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>

Sieht also so aus,

  1. Es wurden mehrere weitere Fossilien des Homo sapiens entdeckt .

  2. Es wurden mehrere weitere Fossilien des Homo sapiens entdeckt.


Algoritms Einführung und Diskussion

Bei " Layout-Lösung " überprüft der einfachste Algorithmus die font-styleCSS-Eigenschaft aller Textblöcke und invertiert sie mit jQuery:

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

Aber dieser Algorithmus überlebt einen etwas komplexeren Test nicht.

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

Der zweiteinfachste Algorithmus ist für eine konkrete Lösung und wurde im Abschnitt "Beispiele" verwendet. Haben Sie zwei Schritte:

  1. Schließen Sie das XHTML-Fragment kursiv ein.
  2. kursive Tags zum Öffnen / Schließen invertieren (z. B. </i>bis <i>).

Das heißt, mit Javascript schreiben,

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

Aber auch nicht bis zum zweiten Test überleben, Gleichgewicht der Tags verlieren ... Der "korrigierte" Algorithmus läuft (!), Ist aber nicht portabel, weder schnell noch elegant. Dies wird hier und im folgenden Beispielabschnitt demonstriert .

Der Punkt!

Die Frage ist also:

Gibt es einen einfachen, guten und generischen Algorithmus (in jedem Browser verwendbar und in eine andere Sprache portierbar)? Sie kennen einen anderen "Swap-Kursiv-Algorithmus"?

PS: "generisch" in dem Sinne, dass ich Ihren Algorithmus sogar in XSLT übersetze. Der Algorithmus muss direkt ausgeglichenen XHTML-Code erzeugen (ohne eine zwischengeschaltete Blackbox wie Tidy).


Motivationen

Ich muss den "Swap-Kursiv-Algorithmus" auf Texteditoren, Server-Parser usw. portieren. In allen Fällen kann ich die Eingabe (und Ausgabe) durch Standard-XHTML und <i>-Tag "normalisieren" .

Ich analysiere XHTML-Text von Prosabüchern und wissenschaftlichen Artikeln, die aus verschiedenen Ursprüngen und Stilen exportiert wurden ... Die meisten Texte werden als "normaler Text" exportiert, aber viele Titel (z. B. Artikeltitel, Kapiteltitel) und manchmal , ein vollständiges Kapitel oder ein vollständiges Textfeld (z. B. Artikelzusammenfassung) sind kursiv dargestellt. Alle diese "mit Kursivschrift stilisiert" müssen invertiert werden. Typische Fälle:

  • Wandeln Sie die ursprüngliche Kursivschrift "Alle Kapitel kursiv" in "Normaltext für alle Kapitel" um: Siehe diesen Fall , in dem in einem Buch mit etwa 300 Seiten 8 der 25 Kapitel invertiert werden müssen.

  • Kursiv gedruckte Anführungszeichen, Abstracts usw. Siehe dieses Beispiel . Müssen Sie wieder normal arbeiten, aber ohne die Betonungswörter zu verlieren.

  • Das Schreiben von Binomialnamen von Arten in wissenschaftlichen Texten wird normalerweise kursiv geschrieben (oder invertiert in einer anderen Schriftart als der für "normalen Text" verwendete). Hunderte von kursiven Titeln (von Artikeln und Artikelabschnitten) von XHTML-exportierten Artikeln müssen an meinem Arbeitsplatz invertiert werden. PS: siehe das Beispiel am Anfang der Frage ("Mehrere weitere Homo sapiens ...").

Ich muss auch den generischen Algorithmus (Ihrer Antwort!) In eine XSLT-Bibliothek übersetzen , in der keine "Tag-Balancing-Korrektur" vorhanden ist.

Beispiele

Implementierung eines nicht generischen "Swap Italics-Algorithmus" in Javascript und PHP . Ein generischer benötigt einen allgemeinen "XML-Interleaving-Algorithmus" ... Hier verwende ich Browser- (DOM) und Tidy-Korrekturen als Alternative zum "Interleaving".

Javascript

Es läuft mit komplexen Eingaben (!). Veranschaulichung durch eine jQuery-Implementierung :

 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, mit ordentlich

Das gleiche wie bei Javascript, das in PHP "übersetzt" wurde - die natürliche Übersetzung verwendet DOMDocument()Klassen und loadHTML/ oder saveXMLMethoden, aber was sich genauso verhält wie die Korrespondenten des Browsers, ist die tidyKlasse . Zeigt die gleichen Ergebnisse (!)

 $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";
Peter Krauss
quelle
Ich kann diese Frage nicht beantworten. Können Sie das klarstellen? Vielleicht auf das Wesentliche verkürzen?
Bobson
Die wesentlichen Punkte sind bei Bulets und Bolds ... Können Sie sie bearbeiten, um sie in die Frage zu stellen, oder mit mehr Nachdruck?
Peter Krauss
Ich habe den gesamten Fragentext umgeschrieben und ein konkretes Problem stärker in den Fokus gerückt.
Peter Krauss
Wenn dies nur zur Anzeige dient (?), Haben Sie darüber nachgedacht, das CSS für die Seite so zu ändern, dass die Standardeinstellung kursiv ist und der Teil innerhalb der Tags nicht?
Hum ... Nicht "nur zur Anzeige", die endgültige Ausgabe ist eine endgültige Datenbank wie die PMC . Ich habe den Abschnitt "Motivationen" bearbeitet.
Peter Krauss

Antworten:

2

Aktualisierung (18. Juni 13): Verwenden Sie diese Antwort, um Algorithmen zu erklären und Schlussfolgerungen zusammenzufassen.


Informationen zu Problemumgehungen für jQuery-Traversing und "Layout-Lösung".

Nach dem @ Wilbert-Kommentar habe ich den "einfachsten Algorithmus" angepasst, um das dynamische Verhalten des zu vermeiden check .prop(), das sich mit der .each()Iteration ändert , und das zu entfernen else. Nach allen Iterationen wird ein "übergeordneter Kursiv" aufgelöst. Siehe hier oder den Code unten.

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

Eine andere Möglichkeit, mit dem dynamischen Verhalten umzugehen, besteht darin, eine statische Eigenschaft zu überprüfen, indem sie prop('tagName')sich nicht ändert. Siehe hier oder den Code unten.

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

Es sind weitere Tests und eine endgültige Analyse erforderlich, um die Stileigenschaften in konkrete <i>Tags zu ändern . Um den Algorithmus zweimal anzuwenden, benötigen wir einige Sorgfalt.


Layout-Lösung

Dies ist keine Lösung für die vorliegende Frage, liefert aber einige gute Hinweise und ist die beste (zumindest die kleinste!) Lösung für das "Layoutproblem"!

Die toggleClass()Methode kann verwendet werden, um von einer "kursiven Klasse" zu einer "normalen Textklasse" zu wechseln. Siehe hier oder den Code unten.

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

Und wir können diesen kleinen Algorithmus zweimal und so oft anwenden, wie wir wollen ... Es ist eine gute Lösung! Aber es ist kein "XML-Algorithmus umschreiben", das CSS ist hier ein Schlüssel :

 .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; }

... Für einen Algorithmus, der <i>Tags transformiert , ist das Problem also noch offen ...

Konkrete Lösung

Eine "100% ige Lösung in reinem XSLT1" (in vielen Fällen getestet!) Basierend auf einer Adaption von @ DanielHaley's . Es ist eine effektive <i>Tag-Transformation.

 <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>

Gliederung als "Drive-by-Event-Algorithmus" in einem Kopiervorgang:

  • iTags entfernen : Kopieren Sie alles von " <i> Ding </i> " als " Ding ".

  • iTags einschließen : Kopieren Sie einen beliebigen Text als " <i> Text </i> ", wenn sich der Text nicht in einem Kontext kursiver Eltern befindet. PS: Text ist ein Endknoten des DOM-Baums.

Schlussfolgerungen

"Layout-Lösungen" eignen sich gut für Texteditoren , verwenden jedoch einige Tricks und nicht strenge Lösungen (unabhängig von Überlappungen, Leistung usw.). Für den XML-Prozess müssen wir uns mit der <i>Transformation von Tags befassen ... Die natürlichen Sprachen zum Ausdrücken des Algorithmus sind also XSLT oder xQuery.

Der mit XSLT implementierte Algorithmus zeigt die Framework-Notwendigkeiten:

  1. der Ahnen- Selektor (Eltern, Großeltern usw.), um zu überprüfen, ob es sich um einen "kursiven Kontext" handelt oder nicht;

  2. der Textknotenzugriff (DOM text());

  3. Entfernen Sie das iTag und fügen Sie es hinzu.

So können wir die Probleme mit jedem Framework sehen.

  • DOM (das W3C-Standardframework): Das DOMDocument::renameNode()für Punkt 3 ist noch nicht implementiert (siehe PHP, Javascript usw.).

  • jQuery: hat keine praktische Funktion für Punkt 2, siehe diese Antwort .

  • XSLT: Das Beste, um den Algorithmus auszudrücken, ist jedoch in keinem Kontext wie Javascript verfügbar.

Ich (oder Sie bitte!) Werde versuchen, den XSLT-Algorithmus mit "reinen DOM2" -Methoden auszudrücken. Diese DOM-Version wird der "generische Algorithmus" sein ... Nun: Wenn die Übersetzung nur für DOM3 gültig ist (unter Verwendung von renameNode und anderen Tricks), lautet die Schlussfolgerung vorerst: "Es gibt KEINEN generischen / übersetzbaren Algorithmus".

Peter Krauss
quelle
1

XSLT-Versuch von https://stackoverflow.com/a/17156452/317052 ...

Ich bin nicht sicher, ob dies alle Fälle abdecken würde, aber Sie könnten dies tun:

XML-Eingabe

<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-Ausgabe

<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>

Eingabe gerendert

Es wurden mehrere weitere Fossilien des Homo sapiens entdeckt .

Es wurden mehrere weitere Fossilien des Homo sapiens entdeckt.

Lass mich allein!

O RIGINAL mit Kursivschrift und OHNE


Ausgabe gerendert

Es wurden mehrere weitere Fossilien des Homo sapiens entdeckt .

Es wurden mehrere weitere Fossilien des Homo sapiens entdeckt.

Lass mich allein!

O RIGINAL mit Kursivschrift und OHNE

Daniel Haley
quelle
Hallo, vielen Dank! Wir haben bereits bei Stackoverflow die Notwendigkeit einiger kleiner Korrekturen an einem "perfekten XSLT" besprochen, daher füge ich meine XSLT-Anpassung bei meiner Antwort hinzu. Auch hier ist das XSLT nicht die "endgültige Antwort" ohne eine "Algorithmusdarstellung" oder Hinweise auf eine allgemeine Übersetzung (siehe Abschnitt "Der Punkt") ... Sie werden also nicht die gesamte Prämie erhalten. Ich füge Ihnen 1 Stimme hinzu und verstehe, dass Sie nach den Kopfgeldregeln dieser Website 50% des Kopfgeldes erhalten ... Bitte überprüfen Sie, ob ich mit dieser Regel falsch liege.
Peter Krauss
-1

Ich würde einfach:

  1. Konvertiere alles <i>in </i>s
  2. Konvertiere alles </i>in <i>s
  3. füge ein <i>am Anfang hinzu
  4. füge ein </i>am Ende hinzu

Damit

 <p id="p1"><i>Several more</i> Homo sapiens <i>fossils were discovered</i>.</p>
 <!-- converts to: -->
 <i><p id="p2">Several more </i>Homo sapiens<i> fossils were discovered.</p></i>
Idioten
quelle
1
Ja, genau das machen die Beispiele (siehe Abschnitt "Beispiele") mit Javascript und PHP, mit Funktionen zum Ersetzen von regulären Ausdrücken. Es gibt auch einen Link, jsfiddle.net/rdfJ5 ... Aber leider ist es keine allgemeine Lösung: Testen Sie Ihren Schritt für Schritt mit diesem Fall, <b><i>O</i>RIGINAL <big><i>with italics</i> and </big> withOUT</b>... er produziert unausgeglichenen (verbotenen!) Code und leere Blöcke .
Peter Krauss
@PeterKrauss also ist das nicht genug für dich? Was ist das Problem?
Idioten
1
(Ich habe den Kommentar bearbeitet, siehe Beispiel, in dem Ihre Schritt-für-Schritt-Anleitung fehlschlägt!). Siehe Abschnitt "Der Punkt": ist nicht allgemein und kann nicht mit XSLT verwendet und / oder von Bibliotheken wiederverwendet werden.
Peter Krauss
<i><b></i>O<i>RIGINAL <big></i>with italics<i> and </big> withOUT</b></i>mag nicht schön sein, aber es funktioniert in jedem Browser
Morons
1
Bitte überprüfen Sie, ob Sie verstehen, was "ausgeglichen" oder "wohlgeformt" ist. Einige Links: Wikipedia , W3C ... Synthese der Regel : "Alle Tags müssen ausgeglichen sein". <i><b></i>ist nicht, und bei ALL DISCUSSION HERE geht es um "einen Algorithmus, der ausgewogene Ergebnisse liefert".
Peter Krauss