【问题标题】:Inserting data in html file using TCL使用 TCL 在 html 文件中插入数据
【发布时间】:2014-05-14 13:57:10
【问题描述】:

这是我的 html 文件:

<head>
<title>Reading from text files</title>
</head>
<body>
<h3>Starting space</h3>
<ul>
    <li></li>
</ul>
<h3>ending space</h3>
<ul>
</body>
</html>

我想使用 tcl 和正则表达式来编辑这个 html 文件。但我想在特定位置编辑它,即在Starting spaceending space 之间。在这些点之间,我想添加各种列表项。

<li> First </li>

etc.. 我编写了 tcl 脚本来打开这个文件,并尝试打印出这两个位置之间的数据,以便我以后可以编辑它。但我无法做到这一点。你能指出我哪里出错了吗?

Tcl 脚本

proc edit_html {release} {
set fp [open $release r]
set para [read -nonewline $fp]
close $fp
set line_read [regexp -nocase -lineanchor -inline -all -- {^\s*?Starting space\s*?.*?ending space} $para]
foreach line_read $line_read {
    regexp -nocase -- {^\s*?Starting space\s*?.*?ending space} $line_read - tag value
    puts $value

}
}
edit_html [lindex $argv 0]

我不确定这个正则表达式哪里出错了。一旦我找到位置,我应该如何编辑它?有什么提示吗?就像我应该带文件指针一样?

【问题讨论】:

    标签: html regex tcl


    【解决方案1】:

    这是我的解决方案:

    proc edit_html {release} {
        set f [open $release]
        while {[gets $f line] != -1} {
            if {[string match "*Starting space*" $line]} {
                puts "FANCY LIST"; # Replace with your fancy list
    
                # Skip to the ending space
                while {![string match "*ending space*" $line]} {
                    gets $f line
                }
            } else {
                puts $line
            }
        }
        close $f
    }
    

    我正在将输出写入控制台,但您可以选择将其写入文件。

    【讨论】:

      【解决方案2】:

      如果您想将项目列表添加到 tcl 模板中,更惯用的方法是将模板创建为字符串,并使用 tcl 的替换机制来填充它。

      set template {
          <head>
          <title>Reading from text files</title>
          </head>
          <body>
          <h3>Starting space</h3>
          <ul>
              [get_listitems]
          </ul>
          <h3>ending space</h3>
          <ul>
          </body>
          </html>
      }
      
      set items {First Second Third}
      
      proc get_listitems {} {
          global items
          set s ""
          foreach i $items {
              append s "<li>$i</li>"
          }
          return $s
      }
      
      subst $template
      

      【讨论】:

        【解决方案3】:

        当前代码的第一个问题是您没有修改任何内容。 regexp 用于读取/获取数据,而不是进行更改。您可能想改用regsub。现在的问题是,如果你想从原始文件中更改它,并且你有很多 Starting spaceending space,你可能需要使用一个函数。

        其次,您的正则表达式不匹配。您没有^\s*?Starting space,但您有^&lt;h3&gt;Starting space,并且您还需要在该正则表达式中编辑其他部分。

        我已经写了下面的过程:

        proc edit_html {release} {
          proc re_sub {block} {
            # Get the items to b replaced
            global items
            # Get the indentation and put in $spaces
            regexp -lineanchor -- {^(\s*)<li>} $block - spaces
            set html_items [list]
            foreach item $items {
              lappend html_items "<li>$item</li>"
            }
            # Create the list of items in html form with indentation
            set html_items [join $html_items "\n$spaces"]
            regsub -lineanchor -- {<li>\s*</li>} $block $html_items result
            return $result
          }
          set fp [open $release r]
          set para [read -nonewline $fp]
          close $fp
          # The command to be executed
          set cmd {[re_sub "\0"]}
          # The substitution
          set result [subst [regsub -all -- {^<h3>Starting space</h3>\s*?.*?\s*?<h3>ending space} $para $cmd]]
          return $result
        }
        # The items to insert
        set items [list First Second Third]
        
        edit_html [lindex $argv 0]
        

        使用包含First Second Third 的名为items 的已定义列表,您将得到以下输出:

        <head>
        <title>Reading from text files</title>
        </head>
        <body>
        <h3>Starting space</h3>
        <ul>
            <li>First</li>
            <li>Second</li>
            <li>Third</li>
        </ul>
        <h3>ending space</h3>
        <ul>
        </body>
        </html>
        

        【讨论】:

        • Jerry:它没有对 html 页面进行任何更改。你确定你没有错过任何东西。我在调试的时候,你能不能也看看?
        • @user3491702 您可以轻松地将结果放入文件中,方法是将最后一行替换为 set f [open "newfile" w]; puts $f [edit_html [lindex $arv 0]]; close $f,或者在 proc 中放入类似的内容而不是 returning 结果。
        • @user3491702 没问题,伙计!
        【解决方案4】:

        要编辑一个文本文件,你需要把它加载到内存中,然后再写出来;您无法通过写回同一个文件进行流式传输。您可以编写一种简单的方法来直接选择要替换的文本,您可以使用regsub 作为其核心,但这在此处是不可能的,因为您要匹配该区域任一侧的文本以进行匹配。因此,对于您正在查看的那种编辑,您需要的是字符串的索引(即文件的内容),它指示要替换的第一个字符的位置,以及最后一个字符的索引换了。

        幸运的是,获取索引很容易。要么使用regexp -indices,要么使用string first/string last

        # Read the file; standard stanza
        set f [open $theFilename]
        set data [read $f]
        close $f
        
        # Find the markers
        regexp -indices {<h3>Starting space</h3>\n<ul>\n} $data start
        regexp -indices {\n</ul>\n<h3>ending space</h3>} $data end
        
        # We now need to offset the ends by one in each direction (we want stuff between)
        set start [expr {[lindex $start 1] + 1}]
        set end [expr {[lindex $end 0] - 1}]
        
        # Now we can generate the replacement...
        set replacement ""
        foreach item ... {
            append replacement "<li>...</li>\n"
        }
        
        # ... and insert it
        set data [string replace $data $start $end $replacement]
        
        # ... and write it out (without the extra newline; we've enough already)
        set f [open $theFilename "w"]
        puts -nonewline $f $data
        close $f
        

        或者,您可以在将内容写回文件时进行替换。

        # Read the file; standard stanza
        set f [open $theFilename]
        set data [read $f]
        close $f
        
        # Find the markers
        regexp -indices {<h3>Starting space</h3>\n<ul>\n} $data start
        regexp -indices {\n</ul>\n<h3>ending space</h3>} $data end
        
        # Generate the replacement text
        set replacement ""
        foreach item ... {
            append replacement "<li>...</li>\n"
        }
        
        # Write everything out
        set f [open $theFilename "w"]
        puts -nonewline $f [string range $data 0 [lindex $start 1]]
        puts -nonewline $f $replacement
        puts -nonewline $f [string range $data [lindex $end 0] end]
        close $f
        

        【讨论】:

        • 效果很好。但是每次我们运行这个,我们将不得不一次又一次地写入文件?
        • @user3491702 您可以轻松地将输出写入另一个文件。甚至微不足道。
        【解决方案5】:

        您已经收到了很多很好的答案,我只想指出,使用正则表达式解析 HTML 可能很棘手且容易出错。然而,tDOM 包使这变得轻而易举。

        您确实需要格式良好的 HTML(但它不必是 XHTML 级别的格式良好),所以我将为 html 元素添加一个起始标记。我还将删除相关ul 中的空li 元素,不是因为tDOM 需要它,而是因为它使我的解决方案更简单:

        <html>
        <head>
        <title>Reading from text files</title>
        </head>
        <body>
        <h3>Starting space</h3>
        <ul>
        </ul>
        <h3>ending space</h3>
        <ul>
        </ul>
        </body>
        </html>
        

        以任何您喜欢的方式将其放入变量中,例如通过从文件中读取:

        set f [open foo.html] ; set html [read -nonewline $f] ; close $f
        

        创建一个文档对象并找到根节点:

        set doc [dom parse -html $html]
        set root [$doc documentElement]
        

        找到您要插入的节点:它是紧随h3 元素的第一个ul 元素,该元素具有值为"Starting space" 的文本节点。

        set xpath {//h3[contains(text(), 'Starting space')]/following-sibling::ul[1]}
        lassign [$root selectNodes $xpath] node
        

        将项目插入此节点。最好有一个命令:

        proc addItem {doc node txt} {
            set li [$doc createElement li]
            $li appendChild [$doc createTextNode $txt]
            $node appendChild $li
        }
        

        现在就做吧:

        addItem $doc $node "First item"
        

        将更改的文档写回原始文件或另一个文件:

        set f [open bar.html w] ; $root asHTML -channel $f ; close $f
        

        (请注意,asHTML 不会美化或保留原始 HTML 的格式。)

        最后清理删除文档对象创建的数据结构和命令:

        $doc delete
        

        旁白:

        如果允许更改原始 HTML 的结构,您可以通过将id 属性添加到要插入的元素来使这更容易和更安全。如果你的 HTML 有这个:

        <ul id="insertitemshere">
        

        xpath 变成类似

        set xpath {//ul[@id='insertitemshere']}
        

        tDOM 包在此处记录:http://tdom.github.io/。它包含在 ActiveState Tcl 发行版中,并且在那里也有记录。

        文档:lassignprocset

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-03-02
          • 2020-07-27
          • 1970-01-01
          • 2016-02-15
          • 2020-01-30
          • 2021-06-19
          • 1970-01-01
          相关资源
          最近更新 更多