Как замаскировать конфиденциальную информацию, содержащуюся в файле, с помощью tcl?

Я пытаюсь реализовать tcl-скрипт, который читает текстовый файл и маскирует всю конфиденциальную информацию (например, пароли, IP-адреса и т. д.), содержащуюся в нем, и записывает вывод в другой файл.

На данный момент я просто заменяю эти данные на ** или ##### и просматриваю весь файл с помощью регулярного выражения, чтобы найти материал, который мне нужно замаскировать. Но поскольку мой текстовый файл может состоять из 100 тысяч строк текста и более, это оказывается невероятно неэффективным.

Существуют ли какие-либо встроенные функции/команды tcl, которые я могу использовать, чтобы сделать это быстрее? Предоставляют ли какие-либо дополнительные пакеты дополнительные параметры, которые могут помочь в этом?

Примечание: я использую tcl 8.4 (но если есть способы сделать это в более новых версиях tcl, пожалуйста, укажите мне на них)


person egorulz    schedule 19.03.2013    source источник
comment
Можете ли вы опубликовать свои регулярные выражения? string match или string first вместе с string replace может быть быстрее. Надо будет протестировать и сравнить.   -  person potrzebie    schedule 19.03.2013


Ответы (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 действительно гибкие).

person Donal Fellows    schedule 19.03.2013

Как уже упоминалось, особенно для очень больших файлов - это не лучший способ прочитать весь файл в переменную. Как только в вашей системе заканчивается память, вы не можете предотвратить сбой приложения. Для данных, разделенных разрывами строк, самым простым решением является буферизация одной строки и ее обработка.

Просто для примера:

# 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] в цикле while.

person Dominic Ernst    schedule 19.03.2013
comment
Не тестировалось, но я считаю, что некоторые языки сценариев (например, Perl) должны работать очень хорошо для такой работы. :) - person pynexj; 19.03.2013
comment
Я никогда не использовал Perl до сих пор, но могу сказать, что скомпилированные программы всегда имеют большую производительность по сравнению со скриптовыми программами - это аксиома. - person Dominic Ernst; 19.03.2013
comment
Не используйте eof в качестве условия while (phaseit.net/claird /comp.lang.tcl/fmm.html#eof) -- идиома TCL для построчного чтения файла: while {[gets $old line] != -1} {... - person glenn jackman; 19.03.2013
comment
Исправил мой код примера, как было предложено - спасибо @glennjackman - person Dominic Ernst; 19.03.2013
comment
Tcl довольно хорошо справляется с обработкой больших файлов, если правильно установить кодировку канала и использовать другие общие методы повышения производительности, такие как размещение обработки в процедуре. (Кроме того, измерьте производительность с различными версиями Tcl; она не постоянна во всех версиях, но то, ускорятся ли вещи или нет, зависит от деталей того, что делается.) - person Donal Fellows; 19.03.2013

Файл со 100 000 строк — это не так уж и много (если только каждая строка не имеет длины 1 000 символов :), поэтому я предлагаю вам 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
person pynexj    schedule 19.03.2013
comment
Если будет удалено больше символов, чем добавлено, вы получите некоторое старое содержимое после окончания нового содержимого. Используйте chan truncate (Tcl 8.5) перед закрытием потока или открытием двух разных файлов. - person potrzebie; 19.03.2013
comment
Даже файл с миллионом символов на самом деле не так уж и много, когда у вас гигабайты памяти… - person Donal Fellows; 19.03.2013