2015-06-19 123 views
2

我試圖從基於使用TCL某些關鍵詞的文件解析選擇性的數據,例如我有這樣如何從TCL文件中獲取選擇性數據?

... 
... 
.. 
... 
data_start 
30 abc1 xyz 
90 abc2 xyz 
214 abc3 xyz 
data_end 
... 
... 
... 

文件如何我只趕上了30,90和214「DATA_START」之間和「data_end」?我到目前爲止(tcl新手),

proc get_data_value{ data_file } { 

set lindex 0 
set fp [open $data_file r] 
set filecontent [read $fp] 



while {[gets $filecontent line] >= 0} { 

if { [string match "data_start" ]} { 

    #Capture only the first number? 
    #Use regex? or something else? 

     if { [string match "data_end" ] } { 

      break 
     } else { 

      ##Do Nothing? 
     } 
    } 
} 
close $fp 
} 

回答

2

如果你的文件尺寸較小,那麼你可以使用read命令對整個數據啜到一個變量,然後應用regexp提取所需的信息。

input.txt中

data_start 
30 abc1 xyz 
90 abc2 xyz 
214 abc3 xyz 
data_end 
data_start 
130 abc1 xyz 
190 abc2 xyz 
1214 abc3 xyz 
data_end 

extractNumbers.tcl

set fp [open input.txt r] 
set data [read $fp] 
close $fp 
set result [regexp -inline -all {data_start.*?\n(\d+).*?\n(\d+).*?\n(\d+).*?data_end} $data] 
foreach {whole_match number1 number2 number3} $result { 
    puts "$number1, $number2, $number3" 
} 

輸出:

30, 90, 214 
130, 190, 1214 

更新:

將較大的文件內容讀入一個變量會導致程序崩潰取決於您的PC的內存。當我嘗試在Win7 8GB RAM筆記本電腦上用read命令讀取大小爲890MB的文件時,我得到了unable to realloc 531631112 bytes錯誤消息,並且tclsh墜毀。經過一些基準測試發現它能夠讀取500,015,901字節大小的文件。但該程序將消耗500MB的內存,因爲它必須保存數據。

另外,當通過regexp提取信息時,有一個變量來保存這麼多的數據並不高效。因此,在這種情況下,最好逐行閱讀內容。

閱讀更多關於here

+0

謝謝Dinesh,這工作完全符合我的需要。如果我有一個非常大的文件,什麼是折衷?只是處理時間? – user2045143

1

將文件中的所有數據加載到變量中。設置開始和結束標記並尋找這些位置。逐行處理項目。 Tcl使用由空格分隔的字符串列表,因此我們可以使用foreach {a b c} $ line {...}處理行中的項目。

TCL:

set data {... 
... 
.. 
... 
data_start 
30 abc1 xyz 
90 abc2 xyz 
214 abc3 xyz 
data_end 
... 
... 
...} 


set i 0 
set start_str "data_start" 
set start_len [string length $start_str] 
set end_str "data_end" 
set end_len [string length $end_str] 

while {[set start [string first $start_str $data $i]] != -1} { 
    set start [expr $start + $start_len] 
    set end [string first $end_str $data $start] 
    set end [expr $end - 1] 
    set item [string range $data $start $end] 
    set lines [split $item "\n"] 

    foreach {line} $lines { 
     foreach {a b c} $line { 
      puts "a=$a, b=$b, c=$c" 
     } 
    } 

    set i [expr $end + $end_len] 
} 

輸出:

a=30, b=abc1, c=xyz 
a=90, b=abc2, c=xyz 
a=214, b=abc3, c=xyz 
1

我會寫,作爲

set fid [open $data_file] 
set p 0 
while {[gets $fid line] != -1} { 
    switch -regexp -- $line { 
     {^data_end} {set p 0} 
     {^data_start} {set p 1} 
     default { 
      if {$p && [regexp {^(\d+)\M} $line -> num]} { 
       lappend nums $num 
      } 
     } 
    } 
} 
close $fid 
puts $nums 

,或者甚至

set nums [exec sed -rn {/data_start/,/data_end/ {/^([[:digit:]]+).*/ s//\1/p}} $data_file] 
puts $nums 
+0

在這種情況下,正則表達式很昂貴,完全沒有要求。執行一個單獨的過程本身就是一種可憎的行爲 - 而且恰巧也使腳本變得不可移植。 –

0

我最喜歡的方法將是對於每個可接受令牌的聲明proc S和利用unknown mechanism靜靜忽視不可接受的。

proc 30 args { 
    ... handle 30 $args 
} 

proc 90 args { 
    ... process 90 $args 
} 

rename unknown original_unknown 
proc unknown args { 
    # This space was deliberately left blank 
} 

source datafile.txt 
rename original_unknown unknown 

您將使用Tcl的內置解析,這應該會更快。我認爲這看起來更好。

你也可以把線處理邏輯到您的unknown - 過程完全:

rename unknown original_unknown 
proc unknown {first args} { 
    process $first $args 
} 
source input.txt 
rename original_unknown unknown 

無論哪種方式,關鍵是TCL的自己的解析器(C實現)將分手的輸入線成令牌 - 所以你不必在Tcl中自己實現解析。

這並不總是有效 - 例如,如果輸入使用多行語法(不使用{}),或者令牌與空白以外的東西分開。但在你的情況下,它應該做得很好。

相關問題