Разбор файла с помощью Tcl

У меня есть файл, в котором есть несколько операторов set. Однако я хочу извлечь интересующие меня строки. Может ли помочь следующий код

set in [open filename r]    
seek $in 0 start    
while{ [gets $in line ] != -1} {    
    regexp (line to be extracted)
}

tcl
person Naaz    schedule 25.03.2011    source источник


Ответы (5)


Другое решение:

Вместо использования gets я предпочитаю использовать функцию read для чтения всего содержимого файла, а затем обрабатывать его построчно. Таким образом, мы полностью контролируем работу с файлом, имея его в виде списка строк.

set fileName [lindex $argv 0]
catch {set fptr [open $fileName r]} ;
set contents [read -nonewline $fptr] ;#Read the file contents
close $fptr ;#Close the file since it has been read now
set splitCont [split $contents "\n"] ;#Split the files contents on new line
foreach ele $splitCont {
    if {[regexp {^set +(\S+) +(.*)} $ele -> name value]} {
        puts "The name \"$name\" maps to the value \"$value\""
    }
}

Как запустить этот код:
скажем, приведенный выше код сохранен в test.tcl
Тогда

tclsh test.tcl FileName

FileName — это полный путь к файлу, если файл не находится в том же каталоге, что и программа.

person vaichidrewar    schedule 25.03.2011
comment
строка для извлечения ...... man.poi o a vs vs s 0.9 0.09 ... я не думаю, что регулярное выражение будет работать - person Naaz; 29.03.2011
comment
Вы указали имя файла в качестве аргумента при запуске программы? Ошибка возникает из-за того, что функция открытия не нашла файл. - person vaichidrewar; 30.03.2011
comment
@Nazeeb, что именно нужно извлечь из man.poi o a vs vs s 0,9 0,09? - person vaichidrewar; 30.03.2011
comment
@Vaichidrewr: нужно извлечь всю строку - person Naaz; 12.04.2011
comment
@vaichidrewar Используйте переключатель чтения -nonewline, если вы собираетесь разделить результат с помощью \n. В противном случае, если файл заканчивается новой строкой, вы получите лишний пустой ложный элемент в конце списка строк. - person potrzebie; 07.10.2012
comment
@potrzebie Спасибо :) Раньше я всегда задавался вопросом, почему я получаю лишний элемент. Я обновил свой ответ. - person vaichidrewar; 07.10.2012

Во-первых, вам не нужно seek в начало сразу после открытия файла для чтения; вот где это начинается.

Во-вторых, шаблон для чтения файла таков:

set f [open $filename]
while {[gets $f line] > -1} {
    # Process lines
    if {[regexp {^set +(\S+) +(.*)} $line -> name value]} {
        puts "The name \"$name\" maps to the value \"$value\""
    }
}
close $f

Хорошо, это очень простой RE посередине (и для более сложных файлов вам понадобится несколько), но это общий шаблон. Обратите внимание, что, как обычно для Tcl, важен пробел после командного слова while, а также пробел между выражением while и телом while. Чтобы получить конкретную справку о том, какой RE использовать для определенных типов входных данных, задайте дополнительные вопросы здесь, в Stack Overflow.

person Donal Fellows    schedule 25.03.2011
comment
У меня есть строки, подобные следующим.... set name some string set othername some otherstring.... и скоро - person Naaz; 25.03.2011
comment
@Nazeeb: попробуйте измененную версию выше. Разбивает строки так, чтобы вы получили $name и $value. Мой образец просто распечатывает их; добавьте код, чтобы сделать что-то умнее... - person Donal Fellows; 25.03.2011
comment
строка для извлечения ...... man.poi o a vs vs s 0.9 0.09 ... я не думаю, что регулярное выражение будет работать - person Naaz; 29.03.2011

Еще одно решение:

так как источник выглядит как скрипт TCL, создайте новый безопасный интерпретатор, используя interp, который имеет только набор открытая команда (и любые другие, которые вам нужны), скройте все остальные команды и замените unknown, чтобы просто пропустить все нераспознанное. источник ввода в этом интерпретаторе

person jk.    schedule 28.03.2011

Вот еще одно решение: используйте функцию сканирования файлов Tclx. Пожалуйста, посмотрите Tclx для получения дополнительной информации. Мне нравится это решение тем, что у вас может быть несколько блоков сканирования.

package require Tclx

# Open a file, skip error checking for simplicity
set inputFile [open sample.tcl r]

# Scan the file 
set scanHandle [scancontext create]
scanmatch $scanHandle {^\s*set} {
    lassign $matchInfo(line) setCmd varName varValue; # parse the line
    puts "$varName = $varValue"
}
scanfile $scanHandle $inputFile
close $inputFile

Еще одно решение: используйте команду grep из пакета fileutil:

package require fileutil

puts [lindex $argv 0]
set matchedLines [fileutil::grep {^\s*set} [lindex $argv 0]]
foreach line $matchedLines {
    # Each line is in format: filename:line, for example
    # sample.tcl:set foo bar
    set varName [lindex $line 1]
    set varValue [lindex $line 2]
    puts "$varName = $varValue"
}

person Hai Vu    schedule 26.03.2011
comment
есть возможность использовать скрипт в TCL вместо TCLx - person Naaz; 28.03.2011
comment
@Nazeeb: это все еще TCL, но с помощью пакета Tclx. Если вы не хотите использовать Tclx, посмотрите другие решения. Надеюсь, я правильно понял ваш вопрос. - person Hai Vu; 28.03.2011

Я прочитал ваши комментарии до сих пор, и если я вас правильно понял, ваш файл входных данных имеет 6 (или 9, в зависимости от комментария) полей данных в строке, разделенных пробелами. Вы хотите использовать регулярное выражение, чтобы разбить их на 6 (или 9) массивов или списков, по одному на поле данных.

Если это так, я бы попробовал что-то вроде этого (используя списки):

set f [open $filename]
while {[gets $f line] > -1} {
    # Process lines
    if {[regexp {(\S+) (\S+) (\S+) (\S+) (\S+) (\S+)} $line -> name source drain gate bulk inst]} {
        lappend nameL $name
        lappend sourceL $source
        lappend drainL $drain
        lappend gateL $gate
        lappend bulkL $bulk
        lappend instL $inst
    }
}
close $f

Теперь у вас должен быть набор из 6 списков, по одному на поле, с одной записью в списке для каждого элемента в вашем входном файле. Например, чтобы получить доступ к i-му имени, вы берете $nameL[$i].

Если (как я подозреваю) ваша основная цель — получить параметры устройства с именем «foo», вы должны использовать такую ​​структуру:

set name "foo"
set i [lsearch $nameL $name]
if {$i != -1} {
    set source $sourceL[$i]
} else {
    puts "item $name not found."
    set source ''  
    # or set to 0, or whatever "not found" marker you like
}
person Erik Johnson    schedule 03.04.2011