Qual o algoritmo correto para inverter itálico em um texto misto?

8

As motivações das perguntas foram descritas na seção abaixo. Existem várias maneiras de colocar o texto em itálico ; portanto, talvez haja mais de um bom " algoritmo de troca de itálico ". O problema revela algumas dificuldades adicionais em um código XHTML, e o uso da <i>tag deve ser equilibrado . Exemplo:

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

Então, fica assim,

  1. Vários outros fósseis do Homo sapiens foram descobertos .

  2. Vários outros fósseis do Homo sapiens foram descobertos.


Introdução e discussão de algoritmos

Para " solução de layout ", o algoritmo mais simples é verificar a font-stylepropriedade CSS de todos os blocos de texto e invertê-los com jQuery:

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

Mas esse algoritmo não sobrevive a um teste um pouco mais complexo,

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

O segundo algoritmo mais simples é para uma solução concreta e foi usado na seção "Exemplos". Tenha duas etapas:

  1. coloque o fragmento XHTML em itálico;
  2. inverter abrir / fechar tags em itálico (por exemplo, </i>para <i>).

Ou seja, escrevendo com Javascript,

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

Mas também não sobrevive ao segundo teste, perdendo o equilíbrio de tags ... O algoritmo "corrigido" é executado (!), Mas não é portátil, nem rápido nem elegante. É demonstrado aqui e na seção de exemplo abaixo.

O ponto!

Então a questão é:

existe um algoritmo simples, bom e genérico (utilizável em qualquer navegador e portátil para outros idiomas)? Você conhece outro "algoritmo de troca de itálico"?

PS: "genérico" no sentido em que eu até traduzo seu algoritmo para XSLT. O algoritmo deve produzir código XHTML balanceado diretamente (sem uma caixa preta intermediária como o Tidy).


Motivações

Preciso portar o "algoritmo de troca de itálico" para editores de texto, analisadores de servidor, etc. Em todos os casos, posso "normalizar a entrada" (e a saída) pelo XHTML e <i>tag padrão .

Estou analisando o texto XHTML de livros de prosa e artigos científicos, exportados de diferentes origens e estilos ... A maioria dos textos é exportada como "texto normal", mas muitos títulos (por exemplo, título do artigo, título do capítulo) e, às vezes , um capítulo completo ou uma caixa de texto completa (por exemplo, resumo do artigo) são estilizados em itálico. Todos estes "estilizados com itálico" devem ser invertidos. Casos típicos:

  • Transforme o "todos os capítulos em itálico" em "todos os capítulos em texto normal": veja este caso , onde em um livro de aproximadamente 300 páginas, 8 dos 25 capítulos precisam ser invertidos.

  • Aspas em itálico, resumos, etc. Veja este exemplo . Precisa voltar ao normal, mas sem perder as palavras de ênfase.

  • Escrever nomes binomiais de espécies , em textos científicos, geralmente é digitado em itálico (ou invertido, em uma fonte diferente daquela usada para o "texto normal"). Centenas de títulos em itálico (de artigos e de seções de artigos) de artigos exportados em XHTML devem ser invertidos no meu local de trabalho. PS: veja o exemplo do início da pergunta ("Vários mais Homo sapiens ...").

Também preciso traduzir o algoritmo genérico (da sua resposta!) Em uma biblioteca XSLT , onde não existe uma "correção de balanceamento de tags".

Exemplos

Implementando em Javascript e PHP um "algoritmo de troca de itálico" não genérico . Um genérico precisa de um "algoritmo de intercalação XML" geral ... Aqui eu uso correções do navegador (DOM) e Tidy, como uma alternativa à "intercalação".

Javascript

É executado com entradas complexas (!). Ilustrando, por uma implementação do 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, com arrumado

O mesmo de Javascript, "traduzido" para PHP - a tradução natural está usando DOMDocument()classe e loadHTML/ saveXMLmethodos, mas o que tem o mesmo comportamento que os correspondentes do navegador é a tidyclasse . Mostra os mesmos resultados (!)

 $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
fonte
Não posso fazer cara ou coroa com essa pergunta. Você pode esclarecer isso? Talvez reduzi-lo ao ponto essencial?
Bobson
Os pontos essenciais são com marcadores e negritos ... Você pode editar para colocá-lo no início da pergunta ou com mais ênfase?
Peter Krauss
Reescrevi o texto inteiro da pergunta, colocando mais foco em um problema concreto.
Peter Krauss
Se for apenas para exibição (?), Você considerou modificar o CSS da página para que o padrão fique em itálico e a parte dentro das tags não?
Hum ... Não "apenas para exibição", a saída final é um banco de dados final, como o PMC . Eu editei a seção "motivações".
precisa

Respostas:

2

Atualização (18 de junho de 13): use esta resposta para explicar algoritmos e resumir conclusões.


Sobre as soluções alternativas de deslocamento e "solução de layout" do jQuery.

Após o comentário do @Wilbert, adaptei o "algoritmo mais simples", para evitar o comportamento dinâmico do check .prop(), que muda com a .each()iteração, removendo o else. Após toda a iteração, um "itálico pai" é resolvido. Veja aqui ou o código abaixo.

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

Outra maneira de lidar com o comportamento dinâmico é verificar uma propriedade estática prop('tagName'), que não muda. Veja aqui ou o código abaixo.

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

Ele precisa de mais testes e precisa de uma análise final para alterar as propriedades do estilo para <i>tags concretas . Para aplicar o algoritmo duas vezes, precisamos de alguns cuidados.


Solução de layout

Esta não é uma solução para a presente pergunta, mas produz algumas boas dicas e é a melhor (pelo menos a menor!) Solução para o "problema de layout"!

O toggleClass()método pode ser usado para trocar de uma "classe em itálico" para uma "classe de texto normal". Veja aqui ou o código abaixo.

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

E podemos aplicar esse pequeno algoritmo duas vezes e quantas vezes quisermos ... É uma boa solução! Mas não é um "algoritmo de reescrita XML", o CSS é uma chave aqui :

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

... Então, para um algoritmo que transforma <i>tags, o problema ainda está aberto ...

Solução de concreto

Uma "solução 100%, em XSLT1 puro" (testada com muitos casos!) Baseada em uma adaptação do @ DanielHaley's . É uma <i>transformação eficaz de tags.

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

Esboçando como uma "unidade por algoritmo de evento" em um processo de cópia:

  • remover itags: copie qualquer coisa de " <i> coisa </i> " como " coisa ".

  • inclua itags: copie qualquer texto como " <i> texto </i> ", quando o texto não estiver em um contexto de pais em itálico. PS: o texto é um nó terminal da árvore DOM.

Conclusões

As "soluções de layout" são boas para os editores de texto , mas usam alguns truques e soluções não rigorosas (independentemente de sobreposições, desempenho etc.). Para o processo XML, precisamos lidar com a <i>transformação de tags ... Então, as linguagens naturais para expressar o algoritmo são XSLT ou xQuery.

O algoritmo implementado com o XSLT mostra as necessidades da estrutura:

  1. o seletor ancestral (pai, avô, etc.), para verificar se é ou não um "contexto em itálico";

  2. o acesso do nó de texto (DOM text());

  3. remova e inclua a itag.

Assim, podemos ver os problemas com cada estrutura.

  • DOM (a estrutura padrão do W3C): o DOMDocument::renameNode()item 3 ainda não foi implementado (consulte PHP, Javascript, etc.).

  • jQuery: não possui uma função conveniente para o item 2, veja esta resposta .

  • XSLT: o melhor para expressar o algoritmo, mas não está disponível em nenhum contexto como Javascript.

Eu (ou você, por favor!) Tentarei expressar o algoritmo XSLT com métodos "DOM2 puro". Essa versão do DOM será o "algoritmo genérico" ... Bem: se a tradução for válida apenas para o DOM3 (usando renameNode e outros truques), a conclusão por enquanto é que "não há algoritmo genérico / traduzível".

Peter Krauss
fonte
1

XSLT tentativa de https://stackoverflow.com/a/17156452/317052 ...

Não tenho certeza se isso abrangeria todos os casos, mas você poderia fazer isso:

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

Saída 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>

Entrada renderizada

Vários outros fósseis do Homo sapiens foram descobertos .

Vários outros fósseis do Homo sapiens foram descobertos.

Me deixe em paz!

O RIGINAL com itálico e semOUT


Saída renderizada

Vários outros fósseis do Homo sapiens foram descobertos .

Vários outros fósseis do Homo sapiens foram descobertos.

Me deixe em paz!

O RIGINAL com itálico e semOUT

Daniel Haley
fonte
Olá, muito obrigado! Já discutimos, no Stackoverflow, a necessidade de algumas pequenas correções em um "XSLT perfeito", então adiciono minha adaptação ao XSLT na minha resposta. Aqui também o XSLT não é a "resposta final" sem uma "representação do algoritmo" ou pistas para tradução geral (consulte a seção "O ponto")) ... Portanto, você não receberá toda a recompensa. Eu adiciono 1 voto a você, entendendo que, pelas regras de recompensa deste site, você receberá 50% da recompensa ... Verifique se estou errado sobre esta regra.
Peter Krauss
-1

Eu simplesmente:

  1. Converter tudo <i>em </i>s
  2. Converter tudo </i>em <i>s
  3. adicione um <i>ao começo
  4. adicione um </i>no final

assim

 <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>
Idiotas
fonte
1
Sim, é exatamente o que os exemplos (consulte a seção "Exemplos") estão fazendo com Javascript e PHP, com funções "substituição de expressão regular". Há também um link, jsfiddle.net/rdfJ5 ... Mas, infelizmente, não é uma solução geral: teste seu passo a passo com este caso, <b><i>O</i>RIGINAL <big><i>with italics</i> and </big> withOUT</b>... produz código desequilibrado (proibido!) E blocos vazios .
precisa
@PeterKrauss, então isso não é suficiente para você? Qual é o problema?
Idiotas
1
(Editei o comentário, veja o exemplo em que seu passo a passo falha!). Consulte a seção "O ponto": não é geral e não pode ser usado com XSLT e / ou reutilizado por bibliotecas.
precisa
<i><b></i>O<i>RIGINAL <big></i>with italics<i> and </big> withOUT</b></i>pode não ser Bonito, mas funciona em todos os navegadores #
1950 idiotas
1
Por favor, verifique se você entende o que é "equilibrado" ou "bem formado". Alguns links: wikipedia , W3C ... Sintetizando a regra : "Todas as tags devem ser balanceadas". <i><b></i>não é, e TODA A DISCUSSÃO AQUI é sobre "um algoritmo que produz resultados balanceados".
Peter Krauss