201310061100


3. Поиск в дневниках (log-файлах)
   ------------------------------

    ..В доме, который построил Джек [1]:

У написанного текста есть одно полезное свойство -- его можно читать позже (когда станет возможным понять его смысл).

Почему мы вначале учимся говорить а затем программировать а не наоборот, или чего нам нехватало до создания PERL? Аргументы Шарп-фана:

1. Сборка мусора -- Этого нехватает где-то ниже языка программирования (в операционной системе?). Языков всегда будет много (см. "Вавилонская башня")

2. Умолчания -- как максимум, можно уговорить на named arguments вроде (x) но (x,y=5).. Но Джек.. только не default variables, вроде $_

3. У Ken Thompson'а и Henry Spencer'а регулярные выражения явно сырые. Larry Wall не забыл сунуть их в духовку.. вынуть ничего не забыли? -- да PREG это *ВЕЩЬ*, но причем тут язык? Как видите между языком и ОС есть место (это валидол.. нет -- .NET)

Ну а сегодня речь пойдет о том, как найти нужную нам информацию в LOG-файле (в текстовом документе). Язык, применяемый для написания запросов к plain-тексту (*.txt) называется "регулярные выражения" (RegEx). На эту тему написано огромное количество заметок, статей и книг. Мне очень понравилась вот эта работа Lincoln Stein'а: "Perl Scripting. Regular Expressions" [2]. 

Есть четыре операции для создания регулярных выражений (Steve Kleene, 1956)

  +------------------------------+----------+--------------------------+
  | Операция                     | Обознач. | Семантика (смысл)        |
  | Пример                       |          |                          |
  +------------------------------+----------+--------------------------+
  | Замыкание Клини (репликация) | *        | 0 или несколько раз      |
  |                              |          |                          |
  | a*    "aaa"  в  "baaac"      |          |                          |
  +------------------------------+----------+--------------------------+
  | Выбор из списка (alteration) | |        | или                      |
  |                              |          |                          |
  | "one|two"                    |          |                          |
  | найдет  "one"  в             |          |                          |
  | "one nigth in Paris"         |          |                          |
  | найдет  "two"  в             |          |                          |
  | "rooster and two hens"       |          |                          |
  +------------------------------+----------+--------------------------+
  | Koнкатенация                 | пусто    | и                        |
  |                              |          |                          |
  | "pen"  то есть               |          |                          |
  | "р" и "e"  и "n"             |          |                          |
  +------------------------------+----------+--------------------------+
  | Группирование   (grouping)   | ()       |                          |
  |                              |          |                          |
  | (one)*                       |          |                          |
  | найдет  "oneoneone"          |          |                          |
  | в "zerooneoneonetwotwot.."   |          |                          |
  +------------------------------+----------+--------------------------+

в дальнейшем в язык добавили метасимволы (^ . $ \d ..), сокращения ("in fact // is just an abbreviation for m//", PERL [2]), срезы (slices, [a-z]) и отходы заготовок (inverted - every symbol, but not digit, [^0-9]), подшаблоны и обратные ссылки (subpatterns () and backreferences \1 \2..), расширение алфавита через червоточину (escape symbols: \t ..), ленивый и жадный просмотр выражений (lazy '?' and greedy 'default' evaluation), октальное и шестнадцатеричное представления байта (\0123 \xab), двухбайтовый юникод (\u) и мультибайтовые символы (UTF-8), а также многое другое.

Язык стал запутанным и трудным для понимания, а библиотеки для использования RegEx содержат эксплойты (дыры) -- золотая жила для хакера.

  
Используем C#
-------------

Регулярные выражения в стиле Микрософт. Как обычно, развитие языка регулярных выражений на одном уровне## привело к усложнению их изучения. Не стоит начинать с изучения возможностей C# в этой области. Однако языком поддерживается минимально необходимый базовый набор, что позволяет его с успехом применять на практике. Не переступить порог сложности поможет проверка выражения lint-подобной программой (существуют online анализаторы) либо языком не поддерживающим (сверх)расширенную схему -- например С (базовый уровень), PERL (обоснованно расширенный уровень).

##) Приведу пример. Изучаем счет на английском:

  zero, one, two, three, four, five, six, seven, eight, nine
  
расширяем набор слов и сортируем их по алфавиту -- изучать стало сложнее, хотя старые возможности сохранились, и даже появились новые:

  eight, eleven, five, four, hundred, nine, one, seven, six, ten, three, thousand, twelve, twenty, two, zero
  
я называю это замусориванием. Если мы никогда не видели первого списка, впечатление будет еще эмоциональнее.
  
Ищем компилятор C# (csc.exe) . В Windows 8.1 Preview - x64, таковой обнаружился в следующих папках (6 компиляторов, если это кажется избыточным, стоит поискать в интернете документы микрософт про "зеленые" и "красные" биты):

  C:\Windows\Microsoft.NET\Framework\v2.0.50727\
  C:\Windows\Microsoft.NET\Framework\v3.5\
  C:\Windows\Microsoft.NET\Framework\v4.0.30319\
  C:\Windows\Microsoft.NET\Framework64\v2.0.50727\
  C:\Windows\Microsoft.NET\Framework64\v3.5\
  C:\Windows\Microsoft.NET\Framework64\v4.0.30319\

Для вывода русского текста, установим  юникодный консольный фонт:

  Defaults -> Console Windows Properties -> Font
  Properties -> Console Windows Properties -> Font

    Size: 18
    Font: Lucida Console

Для его запуска из командной строки, создадим скрипт init.bat.
 
Внимание! В командной строке windows (cmd.exe) компилятор версии 3 и выше более не работоспособен на кодовой странице 65001. Однако скомпилированная программа, содержащая русский текст, работоспособна *исключительно* на кодовой странице 65001.

  REM init.bat

  prompt $D$T$S$$$S
  color b0
  mode con: cols=132 lines=43
  
  REM cd 
  REM SET PATH=%path%;%cd%
  
  cd %HOMEPATH%\Desktop
  
  md PV-12
  cd PV-12
  
  echo @echo %USERNAME% > whoami.bat
  echo notepad %%1 > n.bat

  echo mode con: cp select=437 > csc.bat
  echo C:\Windows\Microsoft.NET\Framework\v2.0.50727\csc %%1 >> csc.bat
  echo mode con: cp select=65001 >> csc.bat

  echo mode con: cp select=437 > csc2.bat
  echo C:\Windows\Microsoft.NET\Framework\v2.0.50727\csc %%1 >> csc2.bat
  echo mode con: cp select=65001 >> csc2.bat

  echo mode con: cp select=437 > csc3.bat
  echo C:\Windows\Microsoft.NET\Framework\v3.5\csc %%1 >> csc3.bat
  echo mode con: cp select=65001 >> csc3.bat

  echo mode con: cp select=437 > csc4.bat
  echo C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc %%1 >> csc4.bat
  echo mode con: cp select=65001 >> csc4.bat

  echo mode con: cp select=437 > csc2-64.bat
  echo C:\Windows\Microsoft.NET\Framework64\v2.0.50727\csc %%1 >> csc2-64.bat
  echo mode con: cp select=65001 >> csc2-64.bat
  
  echo mode con: cp select=437 > csc3-64.bat
  echo C:\Windows\Microsoft.NET\Framework64\v3.5\csc %%1 >> csc3-64.bat
  echo mode con: cp select=65001 >> csc3-64.bat
  
  echo mode con: cp select=437 > csc4-64.bat
  echo C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc %%1 >> csc4-64.bat
  echo mode con: cp select=65001 >> csc4-64.bat
  
  mode con: cp select=65001

и запустим его.

Проверим, как работает наш Шарп.

  $ n hello.cs
  $ csc4-64 hello.cs
  $ hello
  Здравствуй, Мир!
  
  $ type hello.cs
  
  using System;
  
  public class HelloWorld {
    static void Main() {
      Console.WriteLine("Здравствуй, Мир!");
    }
  }

Эхо строчек ввода (Одну ввел -- одну вывел, пока не введем строку нулевой длины: enter, enter)

  using System;
  
  public class HelloWorld {
    static void Main() {
      string line;
      while ((line = Console.ReadLine()) != null && line != "") {
        Console.WriteLine("{0}", line);
      }
    }
  }
  
Подключаем обработчик регулярных выражений (на основе примера с [3]).

  // Данный пример ищет трехбуквенные слова в фразе, заданной строкой str.

  using System;
  using System.Text.RegularExpressions;

  class Program {
    private static void showMatch(string text, string expr) {
      Console.WriteLine("Выражение: " + expr);
      MatchCollection mc = Regex.Matches(text, expr);
      foreach (Match m in mc) {
        Console.WriteLine(m);
      }
    }
    
    static void Main(string[] args) {
      string str = "в чащах юга жил был цитрус да но фальшивый экземпляр";
      Console.WriteLine( str);
      Console.WriteLine("Слова из трех букв: ");
      showMatch(str, @"\b[а-я]{3}[.,;:?!]");
      Console.ReadKey();
    }
  }
  
[1]. Самуил Маршак. Дом, который построил Джек. Москва, изд. Художественная литература, 1968 г.
[2]. http://stein.cshl.org/genome_informatics/regex/index.html
[3]. http://www.tutorialspoint.com/csharp/index.htm
