Przetwarzanie wyrażeń regularnych – użyteczne narzędzia skryptowe

Wyrażenie regularne są niewątpliwie potężnym narzędziem, pozwalającym na przetwarzanie plików tekstowych w poszukiwaniu zawartych w nich interesujących nas informacji. Można poddać tego typu przetwarzaniu na przykład logi systemowe lub aplikacyjne, pliki konfiguracyjne urządzeń i innego rodzaju dane.Phone-Digits

Kryteria wyszukiwania mogą być tak proste, jak zapytania literalnie o konkretny ciąg znaków albo nieco bardziej skomplikowane, bo specyfikujące określony wzorzec ciągu przy pomocy tzw. metaznaków (znaków traktowanych nieliteralnie przez interpreter wyrażeń regularnych, reprezentujących określoną kategorię znaków, ich sekwencję występowania albo sposób osadzenia samych znaków bądź ich sekwencji w analizowanym tekście). Wzorce mogą odnosić się na przykład do dat, numerów telefonów, danych adresowych, numerów PESEL czy numerów seryjnych urządzeń itp.

Istnieje wiele gotowych, systemowych narzędzi do przetwarzania wyrażeń regularnych. Chyba najbardziej znanym jest poczciwy grep, albo sed i awk czy choćby vi na maszynach z unixowym systemem operacyjnym. Posłużyć może do tego celu zresztą dowolny dobry edytor programistyczny. Czy ma sens w tej sytuacji pisanie własnych narzędzi skryptowych?

Otóż ma, bo własne narzędzia można rozwijać zaczynając od implementacji podstawowych funkcjonalności, jak wspomniałem, typowych dla wymienionych przykładowych, gotowych rozwiązań, idąc w kierunku zaawansowanych, dodatkowych operacji na danych wyselekcjonowanych przy pomocy dopasowani do wzorca. Chodzi na przykład o operacje arytmetyczne, statystyczne, kryptograficzne czy też choćby – w najprostszym przypadku – formatowanie tekstu w trybie wsadowym.

W takim też celu napisałem swój prosty skrypt do wizualizacji dopasowań tekstu do wyrażeń regularnych, zdefiniowanych przez użytkownika. Skrypt posiada graficzny interfejs oparty o bibliotekę widgetów Tk. Jego podstawowa funkcjonalność polega na wyróżnianiu czerwonym kolorem czcionki tych wierszy tekstu, które spełniają kryteria określonego wyrażenia regularnego. To proste, lecz przydatne narzędzie można rzecz jasna doskonalić, wprowadzając kolejne ulepszenia, takie jak na przykład:

  • Wizualizowanie nie całych wierszy tekstu, a jedynie ciągów znaków pasujących do wzorca;
  • Liczenie wystąpień dopasowań;
  • Pokazywanie numerów wierszy, w których wystąpiły dopasowania;
  • Anonimizacja albo zaciemnianie danych pasujących do wzorca (na przykład danych osobowych w raportach lub logach);
  • Przeprowadzenie analiz statystycznych dotyczących wystąpień ciągów znaków zgodnych ze wzorcem w analizowanym tekście.

To tylko kilka przykładów możliwych kierunków ewolucji skryptu. Akurat te zmiany dają się bardzo prosto zrealizować i w tym właśnie tkwi potęga programowania skryptowego oraz niewątpliwa przewaga skryptowych narzędzi własnego developmentu nad różnego rodzaju „gotowcami”.

Zachęcam Was do tego rodzaju eksperymentów.

Interfejs programu wygląda tak:

regexp_tool-01W pewnych okolicznościach pomocna może okazać się podpowiedź dotycząca podstaw składni wyrażeń regularnych. Pomoc tego rodzaju jest dostępna w postaci „REGEXP Quick Reference”, po naciśnięciu przycisku „Help me”.
regexp_tool-02

Oto listing kodu źródłowego skryptu:

require 'tk'

root = TkRoot.new do
   title "REGEXP Tool for Forensic purposes - Janusz Nawrat - 2014"
end

file_frame = TkFrame.new(root) do
   relief 'sunken'
   borderwidth 3
   padx 5; pady 5
   grid(:row => 0, :column => 0, :sticky => 'we')
end

file_label = TkLabel.new(file_frame) do
   text 'Filename: '
   grid(:row => 0, :column => 0, :sticky => 'e')
end

filename = TkVariable.new("")
filedata = TkVariable.new("")

file_entry = TkEntry.new(file_frame) do
   textvariable filename
   grid(:row => 0, :column => 1, :sticky => 'we', :padx => 5)
end

file_button = TkButton.new(file_frame) do
   text "Browse"
   command proc { 
      file_entry.delete(0, :end)
      filename = Tk::getOpenFile
      begin
         file_entry.insert(0, filename)
         file = File.new(filename, "r")
         while (line = file.gets)
            filedata.value += line
         end
         file.close
         $result_text.insert(1.0, filedata.value)
      rescue Exception => e
         STDERR.puts "Problem with opening the file: #{filename} for reading\n#{e.message}"
         exit
      end
   }
   grid(:row => 0, :column => 2, :sticky => 'ew', :padx => 2)
end

work_frame = TkFrame.new(root) do
   relief 'sunken'
   borderwidth 3
   padx 5; pady 5
   grid(:row => 1, :column => 0)
end
	
$result_text = TkText.new(work_frame) do
   wrap 'none'
   grid(:row => 1, :column => 0, :columnspan => 3)
end

result_v_scroll_bar = TkScrollbar.new(work_frame, :orient => 'vertical', :command => proc { |*args| $result_text.yview *args })
result_v_scroll_bar.grid(:row => 1, :column => 3, :sticky => 'ns')
$result_text.yscrollcommand( proc { |first,last| result_v_scroll_bar.set(first,last) } )

result_h_scroll_bar = TkScrollbar.new(work_frame, :orient => 'horizontal', :command => proc { |*args| $result_text.xview *args })
result_h_scroll_bar.grid(:row => 2, :column => 0, :columnspan => 4, :sticky => 'ew')
$result_text.xscrollcommand(  proc { |first,last| result_h_scroll_bar.set(first,last) } )

$result_text.tag_configure('matched', :foreground => 'red')

regexp_frame = TkFrame.new(root) do
   relief 'sunken'
   borderwidth 2
   padx 2; pady 2
   grid(:row => 2, :column => 0, :sticky => 'ew')
end

regexp_label = TkLabel.new(regexp_frame) do
   text "Regular expression:"
   grid(:row => 0, :column => 0, :sticky => 'e')
end

regexp = TkVariable.new("")

rgexp_entry = TkEntry.new(regexp_frame) do
   textvariable regexp
   grid(:row => 0, :column => 1, :padx => 5, :sticky => 'ew')
end

qref =<<'REFERENCE' 
[abc] - A single character of: a, b, or c 
[^abc] - Any single character except: a, b, or c 
[a-z] - Any single character in the range a-z 
[a-zA-Z] - Any single character in the range a-z or A-Z 
^ - Start of line 
$ - End of line 
\A - Start of string 
\z - End of string 
. - Any single character 
\s - Any whitespace character 
\S - Any non-whitespace character 
\d - Any digit 
\D - Any non-digit 
\w - Any word character (letter, number, underscore) 
\W - Any non-word character 
\b - Any word boundary 
(...) - Capture everything enclosed 
(a|b) - a or b 
a? - Zero or one of a 
a* - Zero or more of a 
a+ - One or more of a 
a{3} - Exactly 3 of a 
a{3,} - 3 or more of a 
a{3,6} - Between 3 and 6 of a 
---------- 
options: i - case insensitive, m - make dot match newlines  
x - ignore whitespace in regex,  o - perform #{...} substitutions only once 
REFERENCE 

regexp_button = TkButton.new(regexp_frame) do 
   text "Help me" 
   command { msgBox = Tk.messageBox( :type => "ok", :icon => "info", 
      :title => "REGEXP Quick Reference", :message => qref) 
   }
   grid(:row => 0, :column => 2, :sticky => 'ew')
end

oper_frame = TkFrame.new(root) do
   relief 'sunken'
   borderwidth 3
   padx 5; pady 5
   grid(:row => 3, :column => 0, :sticky => 'we')
end

oper_clear = TkButton.new(oper_frame) do
   text "Clear"
   command proc { $result_text.delete(1.0, :end); file_entry.delete(0, :end) }
   grid(:row => 2, :column => 1, :sticky => 'ew')
end

oper_exit = TkButton.new(oper_frame) do
   text "Exit"
   command {exit}
   grid(:row => 2, :column => 2, :sticky => 'ew')
end

oper_exec = TkButton.new(oper_frame) do
   text "Execute"
   command proc { 
      $result_text.delete(1.0, :end)
      filedata.value.each_line { |line|
         if line.match(regexp.value)
            $result_text.insert( :end, line, :matched )
         else
            $result_text.insert( :end, line )
         end
      }
   }
   grid(:row => 2, :column => 0, :sticky => 'ew')
end

TkGrid.columnconfigure( root, 0, :weight => 1 )
TkGrid.rowconfigure( root, 0, :weight => 1 )

TkGrid.columnconfigure( file_frame, 0, :weight => 1 )
TkGrid.columnconfigure( file_frame, 1, :weight => 4 )
TkGrid.columnconfigure( file_frame, 2, :weight => 2 )

TkGrid.columnconfigure( regexp_frame, 0, :weight => 1 )
TkGrid.columnconfigure( regexp_frame, 1, :weight => 4 )
TkGrid.columnconfigure( regexp_frame, 2, :weight => 2 )

TkGrid.columnconfigure( oper_frame, 0, :weight => 1 )
TkGrid.columnconfigure( oper_frame, 1, :weight => 1 )
TkGrid.columnconfigure( oper_frame, 2, :weight => 1 )

Tk.mainloop

Informacje Janusz Nawrat
Just ordinary man who likes thinking...

2 Responses to Przetwarzanie wyrażeń regularnych – użyteczne narzędzia skryptowe

  1. erjytep3rwreyw.pl pisze:

    czemu podswietla linijke z ^cisco nie zgadzaja sie wielkosci liter masz opcje ‚i’?

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Log Out / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Log Out / Zmień )

Facebook photo

Komentujesz korzystając z konta Facebook. Log Out / Zmień )

Google+ photo

Komentujesz korzystając z konta Google+. Log Out / Zmień )

Connecting to %s

TOMASZ WEŁNA

artysta grafik | wykładowca

PRACOWNIA OKO

Szkoła Rysunku Malarstwa i Grafiki DR TOMASZA WEŁNY | KRAKÓW | Plac Matejki 10 | tel 691 81 75 74

Piękno neurobiologii

Blog Jerzego Vetulaniego

Teoria muzyki, zasady muzyki, podstawy muzyki

Teoria muzyki, zasady muzyki, podstawy muzyki - czyli to co każdy amator muzyki wiedzieć powinien :)

Personal Development & Inspirations

Przemyślenia i refleksje, którymi warto się podzielić (blog by Janusz Nawrat)

Business IT Cooperation Platform

Biznes i IT - dwa światy, które muszą współdziałać

%d bloggers like this: