Qual è l'algoritmo corretto per invertire il corsivo in un testo misto?

8

Le motivazioni delle domande sono state illustrate nella sezione seguente. Esistono molti modi per rendere il testo in corsivo , quindi, forse, ci sono più di un bene " swap algoritmo in corsivo ". Il problema rivela alcuni aspetti difficoltà in un codice XHTML e utilizzo del tag <i> , che deve essere bilanciato . Esempio:

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

Quindi, assomiglia a questo,

  1. Molti altri Homo sapiens fossili sono stati scoperti .

  2. Sono stati scoperti molti altri fossili Homo sapiens .

Introduzione e discussione degli algoritmi

Per " soluzione di layout ", l'algoritmo più semplice controlla la proprietà font-style CSS di tutti i blocchi di testo, e invertirli con jQuery:

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

Ma questo algoritmo non sopravvive a un test un po 'più complesso,

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

Il secondo algoritmo più semplice è per una soluzione concreta ed è stato utilizzato nella sezione "Esempi". Hai due passaggi:

  1. racchiude il frammento XHTML in corsivo;
  2. inverti i tag in apertura / chiusura in corsivo (ad esempio </i> in <i> ).

Cioè, scrivendo con Javascript,

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

Ma anche non sopravvivere al secondo test, perdendo l'equilibrio dei tag ... L'algoritmo "corretto" gira (!), Ma non è portatile, né veloce né elegante. È mostrato qui e nella sezione di esempio seguente.

Il punto!

Quindi la domanda è,

ci sono un algoritmo semplice, buono e generico (utilizzabile in qualsiasi browser e portatile in un'altra lingua)? Conosci un altro "algoritmo in corsivo di scambio"?

PS: "generico" nel senso che persino io traduco il tuo algoritmo in XSLT. L'algoritmo deve produrre direttamente codice XHTML bilanciato (senza una blackbox intermedia come Tidy).

Motivazioni

Ho bisogno di portare l'algoritmo "swap italics" in editor di testo, parser server, ecc. In tutti i casi posso "normalizzare l'input" (e l'output) con il tag XHTML standard e <i> .

Sto analizzando il testo XHTML di libri in prosa e articoli scientifici, esportati da origini e stili diversi ... La maggior parte dei testi viene esportata come "testo normale", ma molti titoli (ad esempio il titolo dell'articolo, il titolo del capitolo), e, a volte, un capitolo completo o una casella di testo completa (ad esempio l'articolo astratto) sono stilizzati con corsivo. Tutti questi "stilizzati con corsivo" devono essere invertiti. Casi tipici:

  • Trasforma l'originale "all chapter italics" in "all chapter normal text": leggi questo caso , dove in un libro di circa 300 pagine, 8 dei 25 capitoli devono essere invertiti.

  • Virgolette, abstract, ecc. in corsivo Vedi questo esempio . Hai bisogno di tornare alla normalità, ma senza perdere le parole di enfasi.

  • Scrivere nomi binomiali delle specie , nei testi scientifici, di solito sono scritti in corsivo (o invertiti, in un carattere diverso da quello utilizzato per "testo normale"). Centinaia di titoli in corsivo (di articoli e di sezioni di articoli) di articoli esportati XHTML devono essere invertiti sul mio posto di lavoro. PS: guarda l'esempio dell'inizio della domanda ("Diversi altri Homo sapiens ...").

Ho anche bisogno di tradurre l'algoritmo generico (della tua risposta!) in una libreria XSLT , dove non esiste "correzione del bilanciamento del tag".

Esempi

Implementazione in Javascript e PHP di un non generico "algoritmo di scambio in corsivo". Un generico ha bisogno di un generale "algoritmo di interleaving XML" ... Qui utilizzo le correzioni del browser (DOM) e di Tidy, in alternativa a "interleaving".

JavaScript

Funziona con input complessi (!). Illustrando, tramite un'implementazione 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*)<\/>/mg,'$2'); // clean
 s = s.replace(/<([a-z]+)>(\s*)<\/>/mg,'$2'); // clean remain
 $('#inverted').html(s);  
 // END ALGORITHM

 alert(s);

PHP, con Tidy

Lo stesso di Javascript, "tradotto" in PHP - la traduzione naturale utilizza DOMDocument() class e loadHTML / saveXML methodos, ma ciò che ha lo stesso comportamento dei corrispondenti del browser è il tidy class . Mostra gli stessi risultati (!)

 $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*)<\/>/s', '$2', $s); // clean
  $s = preg_replace('/<([a-z]+)>(\s*)<\/>/s', '$2', $s); // clean remain
  // END ALGORITHM
  echo "\n\n$s";
    
posta Peter Krauss 09.06.2013 - 16:13
fonte

3 risposte

2

Aggiornamento (18 giugno 13): utilizzo di questa risposta per spiegare gli algoritmi e riassumere le conclusioni.

Informazioni sulle soluzioni di attraversamento jQuery e "soluzione di layout".

Dopo il commento @Wilbert ho adattato il "più semplice algoritmo", per evitare il comportamento dinamico di check .prop() , che cambia con .each() iterazione, rimuovendo else . Dopo tutta l'iterazione, viene risolto un "genitore in corsivo". Vedi qui o il codice di seguito.

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

Un altro modo per gestire il comportamento dinamico, è il controllo di una proprietà statica, di prop('tagName') , che non cambia. Vedi qui o il codice di seguito.

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

Servono più test e ha bisogno di un'analisi finale per modificare le proprietà di stile in concreti tag <i> . Per applicare l'algoritmo due volte, abbiamo bisogno di un po 'di attenzione.

Soluzione di layout

Questa non è una soluzione per la domanda attuale, ma produce alcuni buoni indizi, e è la soluzione migliore (almeno la più piccola!) per il "problema di layout"!

Il metodo toggleClass() può essere utilizzato per scambiare da una "classe in corsivo" a una "classe di testo normale". Vedi qui o il codice seguente.

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

E possiamo applicare questo piccolo algoritmo due volte, e tante volte quante ne vogliamo ... È una buona soluzione! Ma non è un "algoritmo XML di riscrittura", il CSS è una chiave qui :

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

... Quindi, per un algoritmo che trasforma i tag <i> , il problema è ancora aperto ...

Soluzione concreta

Una "soluzione al 100%, in puro XSLT1" (testata con molti casi!) basata su un adattamento di @ DanielHaley . È una trasformazione efficace dei tag <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>

Delineare un "algoritmo drive-by-evento" in un processo di copia:

  • rimuovi i tag: copia qualsiasi cosa di " <i> cosa </i> " come " cosa ".

  • include% tagi: copia qualsiasi testo come " <i> testo </i> ", quando il testo non è in un contesto di genitori italici. PS: text è un nodo terminale dell'albero DOM.

Conclusioni

Le "soluzioni di layout" sono valide per editor di testo , ma utilizzano alcuni trucchi e soluzioni non rigorose (indipendentemente dalle sovrapposizioni, dalle prestazioni, eccetera.). Per il processo XML abbiamo bisogno di trattare con <i> tag transform ... Quindi i linguaggi naturali per esprimere l'algoritmo sono XSLT o xQuery.

L'algoritmo implementato con XSLT mostra le necessità del framework:

  1. il selettore antenato (genitore, nonno, ecc.), per verificare se è o meno un "contesto corsivo";

  2. l'accesso al nodo di testo (DOM text() );

  3. rimuovi e includi il tag i .

Quindi, possiamo vedere i problemi con ogni framework.

  • DOM (framework standard W3C): il DOMDocument::renameNode() , per l'elemento 3, non è ancora implementato (vedi PHP, Javascript, ecc.).

  • jQuery: non ha una funzione comoda per l'elemento 2, vedi questa risposta .

  • XSLT: il migliore per esprimere l'algoritmo, ma non è disponibile in qualsiasi contesto come Javascript.

I (o you plase!) proverà ad esprimere l'algoritmo XSLT con i metodi "pure DOM2". Quella versione DOM sarà "l'algoritmo generico" ... Bene: se la traduzione è valida solo per DOM3 (usando renameNode e altri trucchi) la conclusione per ora è che "non ci sono algoritmi generici / traducibili".

    
risposta data 17.06.2013 - 14:52
fonte
1

Tentativo XSLT dal link ...

Non sono sicuro che questo copra tutti i casi, ma puoi farlo:

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

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

Input reso

          Diversi Homo sapiens fossili furono scoperti .

         Sono stati scoperti molti altri fossili Homo sapiens .     

Lasciami in pace!

    

O RIGINAL con corsivo e withOUT

Output reso

       Diversi Homo sapiens fossili sono stati scoperti .

        Molti altri Homo sapiens fossili furono scoperti.    

Lasciami in pace!

   

O RIGINAL con corsivo e withOUT

    
risposta data 18.06.2013 - 00:21
fonte
-1

Vorrei semplicemente:

  1. Converti tutto <i> in </i> s
  2. Converti tutto </i> in <i> s
  3. aggiungi un <i> all'inizio
  4. aggiungi un </i> alla fine

Quindi

 <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>
    
risposta data 17.06.2013 - 17:49
fonte

Leggi altre domande sui tag