Thuật toán chính xác để đảo chữ in nghiêng trong một văn bản hỗn hợp là gì?


8

Các động lực câu hỏi đã được mô tả trong phần dưới đây. Có rất nhiều cách để làm cho văn bản in nghiêng , vì vậy, có lẽ, có nhiều hơn một tốt " swap nghiêng thuật toán ". Vấn đề cho thấy một số yếu tố quảng cáo trong mã XHTML và sử dụng <i>thẻ phải được cân bằng . Thí dụ:

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

Vì vậy, trông như thế này,

  1. Một số hóa thạch Homo sapiens đã được phát hiện .

  2. Một số hóa thạch Homo sapiens đã được phát hiện.


Giới thiệu và thảo luận về Algoritms

Đối với " giải pháp bố cục ", thuật toán đơn giản nhất là kiểm tra thuộc tính font-styleCSS của tất cả các khối văn bản và đảo ngược chúng bằng jQuery:

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

Nhưng thuật toán này không tồn tại đến một thử nghiệm phức tạp hơn một chút,

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

Thuật toán đơn giản thứ hai là cho một giải pháp cụ thể và được sử dụng trong phần "Ví dụ". Có hai bước:

  1. gửi đoạn XHTML vào chữ nghiêng;
  2. nghịch mở / thẻ nghiêng gần (ví dụ. </i>đến <i>).

Đó là, viết bằng Javascript,

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

Nhưng cũng không tồn tại đến thử nghiệm thứ hai, mất cân bằng các thẻ ... Thuật toán "đã sửa" chạy (!), Nhưng không di động, không nhanh hay thanh lịch. Nó được trình bày ở đây , và ở phần ví dụ dưới đây.

Quan điểm!

Vì vậy, câu hỏi là

Có một thuật toán đơn giản, tốt và chung chung (có thể sử dụng trong bất kỳ trình duyệt nào và có thể chuyển sang ngôn ngữ khác) không? Bạn biết một "thuật toán in nghiêng" khác?

PS: "generic" theo nghĩa tôi thậm chí còn dịch thuật toán của bạn sang XSLT. Thuật toán phải tạo mã XHTML cân bằng trực tiếp (không có hộp đen trung gian như Tidy).


Động lực

Tôi cần chuyển "thuật toán in nghiêng" sang trình soạn thảo văn bản, trình phân tích cú pháp máy chủ, v.v. Trong mọi trường hợp tôi có thể "bình thường hóa đầu vào" (và đầu ra) bằng <i>thẻ XHTML và thẻ tiêu chuẩn .

Tôi đang phân tích văn bản XHTML của sách văn xuôi và bài báo khoa học, được xuất từ ​​các nguồn gốc và phong cách khác nhau ... Hầu hết các văn bản được xuất dưới dạng "văn bản bình thường", nhưng rất nhiều tiêu đề (ví dụ như tiêu đề bài viết, tiêu đề chương), và, đôi khi , một chương đầy đủ hoặc một hộp văn bản đầy đủ (ví dụ: tóm tắt bài viết) được cách điệu bằng chữ in nghiêng. Tất cả những "cách điệu với chữ nghiêng" phải được đảo ngược. Các trường hợp điển hình:

  • Chuyển đổi "tất cả các chữ in nghiêng" ban đầu thành "tất cả các chương văn bản thông thường": xem trường hợp này , trong một cuốn sách dài khoảng 300 trang, 8 trong số 25 chương cần phải được đảo ngược.

  • Dấu ngoặc kép in nghiêng, tóm tắt, vv Xem ví dụ này . Cần thay đổi trở lại bình thường, nhưng không mất các từ nhấn mạnh.

  • Viết tên nhị thức của các loài , trong các văn bản Khoa học, thường là kiểu chữ in nghiêng (hoặc đảo ngược, trong một phông chữ khác với chữ được sử dụng cho "văn bản bình thường"). Hàng trăm tiêu đề in nghiêng (của các bài báo và các phần bài viết) của các bài viết xuất XHTML phải được đảo ngược tại nơi làm việc của tôi. PS: xem ví dụ về phần đầu của câu hỏi ("Một số Homo sapiens nữa ...").

Tôi cũng cần dịch thuật toán chung (câu trả lời của bạn!) Sang thư viện XSLT , trong đó không tồn tại "hiệu chỉnh cân bằng thẻ".

Ví dụ

Triển khai trong Javascript và PHP một thuật toán "Hoán đổi chữ nghiêng" không chung chung . Một cái chung cần một "thuật toán xen kẽ XML" chung ... Ở đây tôi sử dụng các chỉnh sửa (DOM) và Tidy của trình duyệt, như là một cách thay thế cho "xen kẽ".

Javascript

Nó chạy với đầu vào phức tạp (!). Minh họa, bằng cách triển khai 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, với Tidy

Tương tự Javascript, "được dịch" sang PHP - bản dịch tự nhiên đang sử dụng DOMDocument()lớp và loadHTML/ saveXMLphương thức, nhưng những gì có cùng hành vi so với các phóng viên của trình duyệt là tidylớp . Hiển thị kết quả tương tự (!)

 $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";

Tôi không thể làm cho đầu hoặc đuôi của câu hỏi này. Bạn có thể làm rõ nó? Có thể rút ngắn nó đến điểm cần thiết?
Bobson

Những điểm cốt yếu là với các dấu phẩy và đậm ... Bạn có thể chỉnh sửa để đặt nó vào câu hỏi bắt đầu hoặc nhấn mạnh hơn không?
Peter Krauss

Tôi viết lại toàn bộ văn bản câu hỏi, tập trung hơn vào một vấn đề cụ thể.
Peter Krauss

Nếu đây chỉ là để hiển thị (?), Bạn đã xem xét sửa đổi CSS cho trang để mặc định được in nghiêng và phần trong các thẻ không phải là?

Hum ... Không phải "chỉ để hiển thị", đầu ra cuối cùng là cơ sở dữ liệu cuối cùng, như PMC . Tôi đã chỉnh sửa phần "động lực".
Peter Krauss

Câu trả lời:


2

Cập nhật (ngày 18 tháng 6 năm 13): sử dụng câu trả lời này để giải thích các thuật toán và tóm tắt kết luận.


Giới thiệu về giải pháp chuyển đổi jQuery và "giải pháp bố trí".

Sau khi nhận xét @Wilbert, tôi đã điều chỉnh "thuật toán đơn giản nhất", để tránh hành vi động của check .prop(), thay đổi với .each()phép lặp, loại bỏ else. Sau tất cả các lần lặp lại, một "in nghiêng cha mẹ" sẽ giải quyết. Xem ở đây hoặc mã dưới đây.

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

Một cách khác để đối phó với hành vi động, là kiểm tra một thuộc tính tĩnh, bằng cách prop('tagName'), không thay đổi. Xem ở đây hoặc mã dưới đây.

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

Nó cần nhiều thử nghiệm hơn và cần một phân tích cú pháp cuối cùng để thay đổi các thuộc tính kiểu thành <i>các thẻ cụ thể . Để áp dụng thuật toán hai lần, chúng tôi cần một số chăm sóc.


Giải pháp bố trí

Đây không phải là một giải pháp cho câu hỏi hiện tại, nhưng tạo ra một số manh mối tốt, và là giải pháp tốt nhất (ít nhất là nhỏ nhất!) Cho "vấn đề bố cục"!

Các toggleClass()phương pháp có thể được sử dụng để trao đổi từ một "lớp in nghiêng" sang "lớp bình thường-text". Xem ở đây hoặc mã dưới đây.

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

Và chúng ta có thể áp dụng thuật toán nhỏ này hai lần, và rất nhiều lần chúng ta muốn ... Đó là một giải pháp tốt! Nhưng nó không phải là "thuật toán viết lại XML", CSS là một chìa khóa ở đây :

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

... Vì vậy, đối với thuật toán chuyển đổi <i>thẻ, vấn đề vẫn đang mở ...

Giải pháp bê tông

Một "giải pháp 100%, trong XSLT1 thuần túy" (đã được thử nghiệm với nhiều trường hợp!) Dựa trên sự điều chỉnh của @ DanielHaley . Nó là một <i>thẻ chuyển đổi hiệu quả .

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

Phác thảo là "ổ đĩa theo thuật toán sự kiện" trong quy trình sao chép:

  • xóa ithẻ: sao chép bất kỳ điều gì của " <i> điều </i> " là " điều ".

  • bao gồm icác thẻ: sao chép bất kỳ văn bản nào dưới dạng "<i> văn bản </i> ", khi văn bản không nằm trong ngữ cảnh của cha mẹ in nghiêng. PS: text là nút cuối của cây DOM.

Kết luận

"Giải pháp bố cục" là tốt cho trình soạn thảo văn bản , nhưng họ sử dụng một số thủ thuật và giải pháp không nghiêm ngặt (không liên quan đến sự chồng chéo, hiệu suất, v.v.). Đối với quy trình XML, chúng ta cần xử lý <i>các biến đổi thẻ ... Vì vậy, các ngôn ngữ tự nhiên để thể hiện thuật toán là XSLT hoặc xQuery.

Thuật toán được triển khai với XSLT cho thấy sự cần thiết của khung:

  1. bộ chọn tổ tiên (cha mẹ, ông bà, v.v.), để kiểm tra xem đó có phải là "bối cảnh in nghiêng" hay không;

  2. text()truy cập nút văn bản (DOM );

  3. loại bỏ và bao gồm i thẻ.

Vì vậy, chúng ta có thể thấy các vấn đề với từng khung.

  • DOM (khung tiêu chuẩn W3C): DOMDocument::renameNode()đối với mục 3, chưa được triển khai (xem PHP, Javascript, v.v.).

  • jQuery: không có chức năng thuận tiện cho mục 2, xem câu trả lời này .

  • XSLT: tốt nhất để thể hiện thuật toán, nhưng không có sẵn trong bất kỳ ngữ cảnh nào như Javascript.

Tôi (hoặc bạn plase!) Sẽ cố gắng thể hiện thuật toán XSLT bằng các phương thức "thuần DOM2". Phiên bản DOM đó sẽ là "thuật toán chung" ... Chà: nếu bản dịch chỉ hợp lệ cho DOM3 (sử dụng renameNode và các thủ thuật khác) thì kết luận hiện tại là "KHÔNG có thuật toán chung / có thể dịch".


1

Nỗ lực XSLT từ https://stackoverflow.com/a/17156452/317052 ...

Tôi không chắc chắn nếu điều này sẽ bao gồm tất cả các trường hợp, nhưng bạn có thể làm điều này:

Đầu vào 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>

Đầu ra 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>

Kết xuất đầu vào

Một số hóa thạch Homo sapiens đã được phát hiện .

Một số hóa thạch Homo sapiens đã được phát hiện.

Để tôi yên!

O RIGINAL với chữ nghiêng và KHÔNG


Kết xuất đầu ra

Một số hóa thạch Homo sapiens đã được phát hiện .

Một số hóa thạch Homo sapiens đã được phát hiện.

Để tôi yên!

O RIGINAL với chữ nghiêng KHÔNG


Xin chào, cảm ơn rất nhiều! Chúng tôi đã thảo luận, tại Stackoverflow, sự cần thiết của một số chỉnh sửa nhỏ đối với "XSLT hoàn hảo", vì vậy tôi thêm điều chỉnh XSLT vào câu trả lời của mình. Ngoài ra, XSLT không phải là "câu trả lời cuối cùng" mà không có "mô tả thuật toán" hoặc manh mối cho bản dịch chung (xem phần "Điểm") ... Vì vậy, bạn sẽ không truy xuất tất cả tiền thưởng. Tôi thêm 1 phiếu bầu cho bạn, hiểu rằng, theo quy tắc tiền thưởng của trang web này, bạn sẽ nhận được 50% tiền thưởng ... Vui lòng kiểm tra xem tôi có sai về quy tắc này không.
Peter Krauss

-1

Tôi chỉ đơn giản là:

  1. Chuyển đổi tất cả <i> thành </i>s
  2. Chuyển đổi tất cả </i> thành <i>s
  3. thêm một <i> từ đầu
  4. thêm một </i> kết thúc

Vì thế

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

1
Đúng, chính xác là những gì các ví dụ (xem phần "Ví dụ") đang làm với Javascript và PHP, với các hàm "thay thế biểu thức chính quy". Ngoài ra còn có một liên kết, jsfiddle.net/rdfJ5 ... Nhưng, thật không may, đó không phải là một giải pháp chung: kiểm tra từng bước của bạn với trường hợp này, <b><i>O</i>RIGINAL <big><i>with italics</i> and </big> withOUT</b>... nó tạo ra mã cân bằng (bị cấm!) Và các khối trống .
Peter Krauss

@PeterKrauss vì vậy điều này không đủ cho bạn? Có vấn đề gì vậy?
Morons

1
(Tôi đã chỉnh sửa nhận xét, xem ví dụ về việc từng bước của bạn không thành công!). Xem phần "Điểm": không chung chung và không thể được sử dụng với XSLT và / hoặc được các thư viện sử dụng lại.
Peter Krauss

<i><b></i>O<i>RIGINAL <big></i>with italics<i> and </big> withOUT</b></i>có thể không đẹp nhưng nó hoạt động trong mọi trình duyệt
Morons

1
Vui lòng kiểm tra xem bạn có hiểu thế nào là "cân bằng" hay "được định hình tốt" không. Một số liên kết: wikipedia , W3C ... Tổng hợp quy tắc : "Tất cả các thẻ phải được cân bằng". <i><b></i>thì không, và TẤT CẢ THẢO LUẬN TẠI ĐÂY là về "một thuật toán tạo ra kết quả cân bằng".
Peter Krauss
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.