Как замаскировать конфиденциальную информацию, содержащуюся в файле, используя tcl?
Я пытаюсь реализовать сценарий tcl, который читает текстовый файл и маскирует всю конфиденциальную информацию (например, пароли, IP-адреса и т. Д.), Содержащуюся в нем, и записывает вывод в другой файл.
На данный момент я просто заменяю эти данные ** или ##### и выполняю поиск по всему файлу с помощью регулярного выражения, чтобы найти материал, который мне нужно замаскировать. Но так как мой текстовый файл может содержать 100 тыс. Строк текста или более, это оказывается невероятно неэффективным.
Существуют ли какие-либо встроенные функции / команды tcl, которые я могу использовать, чтобы сделать это быстрее? Предоставляют ли какие-либо дополнительные пакеты дополнительные опции, которые могут помочь сделать это?
Примечание: я использую tcl 8.4 (но если есть способы сделать это в более новых версиях tcl, пожалуйста, укажите мне на них)
3 ответа
Вообще говоря, вы должны поместить свой код в процедуру, чтобы получить максимальную производительность от Tcl. (У вас есть еще несколько связанных опций в 8.5 и 8.6, таких как лямбда-термины и методы класса, но они тесно связаны с процедурами.) Вы также должны быть осторожны с рядом других вещей:
- Положите выражения в фигурные скобки (
expr {$a + $b}
вместоexpr $a + $b
), поскольку это позволяет гораздо более эффективную стратегию компиляции. - Тщательно выбирайте кодировки каналов. (Если вы делаете
fconfigure $chan -translation binary
, этот канал будет передавать байты, а не символы. Тем не мение,gets
не очень эффективен на байтово-ориентированных каналах в 8.4. С помощью-encoding iso8859-1 -translation lf
даст большую часть преимуществ там.) - Tcl довольно хорошо выполняет канальную буферизацию.
- Возможно, стоит сравнить ваш код с различными версиями Tcl, чтобы увидеть, какая из них работает лучше всего. Попробуйте использовать сборку tclkit для тестирования, если вы не хотите (незначительную) сложность иметь несколько интерпретаторов Tcl, установленных только для тестирования.
Идиоматический способ сделать линейно-ориентированные преобразования:
proc transformFile {sourceFile targetFile RE replacement} {
# Open for reading
set fin [open $sourceFile]
fconfigure $fin -encoding iso8859-1 -translation lf
# Open for writing
set fout [open $targetFile w]
fconfigure $fout -encoding iso8859-1 -translation lf
# Iterate over the lines, applying the replacement
while {[gets $fin line] >= 0} {
regsub -- $RE $line $replacement line
puts $fout $line
}
# All done
close $fin
close $fout
}
Если файл достаточно мал, чтобы он мог легко поместиться в памяти, это более эффективно, поскольку весь цикл сопоставления-замены поднимается до уровня C:
proc transformFile {sourceFile targetFile RE replacement} {
# Open for reading
set fin [open $sourceFile]
fconfigure $fin -encoding iso8859-1 -translation lf
# Open for writing
set fout [open $targetFile w]
fconfigure $fout -encoding iso8859-1 -translation lf
# Apply the replacement over all lines
regsub -all -line -- $RE [read $fin] $replacement outputlines
puts $fout $outputlines
# All done
close $fin
close $fout
}
Наконец, регулярные выражения не обязательно являются самым быстрым способом сопоставления строк (например, string match
намного быстрее, но принимает гораздо более ограниченный тип шаблона). Преобразование одного стиля кода замены в другой и быстрое его выполнение не является на 100% тривиальным (RE действительно гибкие).
Файл с 100K строк не так уж много (если каждая строка не длиннее 1K символов:), поэтому я бы посоветовал вам read
весь файл в var и сделайте подстановку в этом var:
set fd [open file r+]
set buf [read $fd]
set buf [regsub -all $(the-passwd-pattern) $buf ****]
# write it back
seek $fd 0; # This is not safe! See potrzebie's comment for details.
puts -nonewline $fd $buf
close $fd
Особенно для очень больших файлов - как уже упоминалось - это не лучший способ прочитать весь файл в переменную. Как только в вашей системе заканчивается память, вы не можете предотвратить сбои приложения. Для данных, разделенных переносами строк, самое простое решение - поместить одну строку в буфер и обработать ее.
Просто чтобы дать вам пример:
# Open old and new file
set old [open "input.txt" r]
set new [open "output.txt" w]
# Configure input channel to provide data separated by line breaks
fconfigure $old -buffering line
# Until the end of the file is reached:
while {[gets $old ln] != -1} {
# Mask sensitive information on variable ln
...
# Write back line to new file
puts $new $ln
}
# Close channels
close $old
close $new
Я не могу придумать лучшего способа обработки больших файлов в Tcl - пожалуйста, не стесняйтесь сообщить мне какое-нибудь лучшее решение. Но Tcl не был создан для обработки больших файлов данных. Для реальной производительности вы можете использовать скомпилированный язык вместо скриптового языка программирования.
Изменить: Заменено ![eof $old]
в то время как цикл.