środa, 31 lipca 2013

Testowanie zgodności ciągów tekstowych- wyrażenia regularne RegExp- 3/4

Zanim uruchomimy pierwszą pełną procedurę z wykorzystaniem techniki wyrażeń regularnych (RegExp) przyjrzyjmy się dodatkowo właściwościom obiektu RegExp

.Pattern       pozwoli nam zdefiniować wzorzec porównawczy,
.Global        określa, czy wzorzec dotyczy pierwszego (= False), 
               czy wszystkich wystąpień w tekście (= True),
.IgnoreCase    określa sposób testowania wyrażenia ze względu na wielkość liter (domyślnie =False,
               wyszukiwanie binarne uwzględniające wielkość liter),

oraz metodom obiektu Regexp:
.Test       zwraca informację czy można dopasować wyrażenie do ciągu tekstowego,
.Replace    dokonuje zamiany fragmentu wzorcowego na nowy wskazany ,
.Execute    wykonuje operację zwracając obiekt Match z kolekcji MatchCollection.

W dużym skrócie podsumowując powyższe elementy obiektu  RegExp określę to w sposób następujący- pozwalają one na wskazanie wzorca a następnie dokonanie zamiany fragmentu tekstu na inny lub pobranie poszczególnych elementów w całości lub dla pierwszego wystąpienia.

Czas na zebranie całości zagadnienia w konkretne rozwiązania i prezentację przykładowych pełnych konstrukcji kodu VBA. Wykonując poniższe operacje w środowisku VBA proszę nie zapomnieć o Kroku 1, a więc ustanowieniu referencji do biblioteki RegExp.

Przykład 1. zamiana wyrazów w ciągu tekstowy, wyraz 'jest' na 'był'


Przykład 2. z podanego ciągu tekstowego pozostawić tylko cyfry
 

Przykład 3. z podanego ciągu tekstowego usunąć wszystkie liczby


Przykład 4. W przykładzie nr 2 dodatkowo pozostały podwójne spacje oraz zbędna spacja przed kropką i wykrzyknikiem. To wszystko także chcemy usunąć i wyprostować. W tej sytuacji utworzymy kaskadowy mechanizm RegExp:

Przykład 5. W powyższym przykładzie zastosowana została technika zamiany kolejności wyrazów. Jest to jedna z wielu dodatkowych korzyści pracy z RegExp. Poniżej prosty przykład, który zamienia kolejność 3 pierwszych wyrazów podanego tekstu:

Przykład 6. Z podanego tekstu chcemy pobrać tylko adres e-mai. W tym celu wykorzystamy jeden z wielu możliwych wzorców odpowiadających adresowi e-mail i stworzymy następujący kod:

Powyższy przykład zamiast metody .Replace wykorzystuje metodę .Execute. Ale o tym napiszę już w kolejnym wpisie za kilka dni...


Osobom, które są zainteresowane wyrażeniami regularnymi polecam poniższe linki, które pozwolą na poszerzenie i ugruntowanie informacji przedstawionych w ostatnich postach. Linki te poświęcone są ogólni RegExp i niekoniecznie wskazują na bezpośrednie wykorzystanie tej techniki w VBA.

Korzystanie z wyrażeń regularnych w programie Microsoft Visual Basic 6.0Regular-Expressions.Info
.NET Framework Regular Expressions
RegExp Help
Regular-Expressions.info
Introduction to Regular Expressions

poniedziałek, 22 lipca 2013

Testowanie zgodności ciągów tekstowych- wyrażenia regularne RegExp- 2/4

Kontynuując temat tworzenia wyrażeń regularnych RegExp omówię najważniejszą umiejętność związaną z tą techniką, a więc budowanie wzorców porównawczych.
 

Krok 2.
To najtrudniejsza część pracy z RegExp- budowa składni tworzącej kryteria. Spojrzymy na to zadanie najpierw przez pryzmat wybranych znaków (kodów) specjalnych, następnie przedstawię proste przykłady, a na koniec zaprezentuję kilka złożonych wzorców.

A) Znaki i symbole specjalne
\d    cyfra
\D    nie cyfra
\s    spacja
\S    nie spacja
\w    znak alfanumeryczny, litera
\W    nie znak alfanumeryczny
\b    granica słowa, pozycja między słowem a spacją
\n    znak nowej linii (vbCrLf, Chr(10), Chr(13), vbNewLine)
\r    znak nowej linii (Chr(13))
\t    znak tabulacji (vbTab)
.     (kropka) dowolny znak z wyjątkiem znaku nowej linii
*     powtórzenie symbolu dowolną ilość lub zero razy
?     powtórzenie symbolu zero lub jeden raz
+     powtórzenie poprzedniego wyrażenia co najmniej raz
\     używany jako znak wyjścia dla znaków specjalnych
^     na początku tekstu
      także symbol zaprzeczenia(patrz poniżej)
$     na końcu tekstu
[]    przedział znaków
      [A-Z]  dowolna wielka litera
      [a-z]  dowolna mała litera
      [0-9]  dowolna cyfra
      [^0-9] nie zawiera cyfry
{}    wskazuje ilość dopuszczalnych powtórzeń: 
      a{1}   jednokrotne powtórzenie litery a
      a{0,2} zero do dwóch powtórzeń litery a
()    grupowanie
|     alternatywa dla grupowania

Proszę jednocześnie pamiętać, że dla prostych wyrażeń bez udziału powtórzeń i znaków specjalnych zastosujemy technikę podobną do tej opisanej przy pracy z operatorem Like w jednym z wcześniejszych postów. RegExp jest także, co do zasady, wrażliwy na wielkość liter

Spójrzmy na przykłady:
RegExp Pattern   Dopasowanie    Wyjaśnienie (opcjonalne)
           (czerwonym-dopasowanie)
abc              abcabcabc
234              1234567
Jan              Jan
                 Janek
                 janek          wielkość liter ma znaczenie
ab*c             acde
                 abcde
                 abbbbcde
ab?c             acde
                 abcde
                 abbbcde        brak dopasowania
ab+c             acde           brak dopasowania
                 abcde
                 abbbcde
ab.d             abcd
                 ab1d
                 abc1d          brak dopasowania 
a.*d             ad
                 abcd
                 asfas13wbd
\d\d             10
                 99
                 100
                 9999           dwa podwójne układy cyfr
\w+              alfa           dowolny, każdy wyraz
                 Witaj Jan! 
\w+@\w+          a@a            uproszczony schemat dla e-maila
                 email@domena.pl
\w+@\w+.\w+      email@domena.pl
                 jan.kowalski@domena.pl
^ab.*c           abc
                 ab123fdcdef
                 babc           tekst nie zaczyna się od a
^ab.*c$          abc
                 ab123c
                 ab123cd        nie kończy się na c
                 ab123cdc       powtórzenie c nie ma znaczenia
                 bacdc          nie zaczyna się od a
^\d              1abc           zaczyna się od cyfry
\d$              abc1           kończy się cyfrą
[abc]            abc            znaki wskazane w nawiasie
                 abcdef
                 abcdefabc   
[^abc]           abcdefabc      nie a, nie b, nie c     

[A-Z]            abcDEF         wielkie litery
                 ABCDEF
                 abcdef
^[A-Z].+         Abc123d        zaczyna się od wielkiej litery
                 abc123d
[+*?.]           ..??**++       nawiasy likwidują znaczenie znaków specjalnych
^[1-9][0-9]*$    1              liczba całkowita większa od zera
                 1000
                 -1000
                 123.45         tylko liczba całkowita
(ab)+            ab
                 ababab
                 bababa
(ab|ba)+         ab
                 ba
                 ababab         rozpozna jako jeden ciąg
                 bacbacab       rozpozna trzy ciągi
                 abcdef
(abc)?           abc            ale także dla pustego ciągu
a(\d+)c          a123c          zawiera cyfry pomiędzy a i c
                 a1c
                 ac
                 abc  
a{2}             alfaabeta
a{1,2}           alfaabeta

Inne gotowe przykłady z tego źródła:
A) weryfikacja, czy podano prawidłowy e-mail:
^[A-Za-z0-9_\.-]+@[A-Za-z0-9_\.-]+[A-Za-z0-9_][A-Za-z0-9_]$

Wskazówka! w sieci znaleźć można wiele przykładów weryfikacji adresów e-mail, kolejny z nich podam w trzeciej lekcji poświęconej RegExp. Zainteresowanych tym zagadnieniem odsyłam do tej dyskusji.

B) weryfikacja zapisu daty w układzie: yyyy-MMM -dd, gdzie MMM to trzyliterowy skrót zapisany z wielkiej litery:
^\d\d\d\d-[A-Z][a-z][a-z]-\d\d$

C)  weryfikacja daty jak powyżej rozszerzona, sprawdzamy, czy miesiąc podany w języku polskim lub angielskim:
^20\d\d-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(0[1-9]|[1-2][0-9]|3[01])$ 

^20\d\d-(Sty|Lut|Mar|Kwi|Maj|Cze|Lip|Sie|Wrz|Paź|Lis|Gru)-(0[1-9]|[1-2][0-9]|3[01])$


środa, 17 lipca 2013

Testowanie zgodności ciągów tekstowych- wyrażenia regularne RegExp- 1/4

W trzech kolejnych postach poświęconych tematyce testowania zgodności ciągów tekstowych przedstawię technikę zwaną potocznie: 'wyrażenia regularne' (ang. regular expressions, powszechnie używane skróty to także RegExp czy RegEx). Wprawne oko czytającego mogło dostrzec, że ten i kolejny post w temacie pracy z tekstem zmieniły tag określający poziom zagadnienia. Temat ten należy bowiem zdecydowanie zaliczyć do zaawansowanych umiejętności programowania.

Zagadnienie związane z wyrażeniami regularnymi (od teraz zwanymi RegExp) jest tematem bardzo obszernym. Postaram się możliwie krótko i prosto przedstawić zagadnienie na tyle, aby móc na końcu zaprezentować praktyczne wykorzystanie. Wszystkich zainteresowanych odsyłam do linków, które zostaną podane pod koniec lub do własnych poszukiwań w Internecie.

Zanim przejdziemy do omówienia zagadnienia kroku po kroku krótka odpowiedź na pytanie 'do czego szczególnie przyda się umiejętność wykorzystania RegExp?':

A) cel podstawowy- weryfikacji, czy dany ciąg tekstu spełnia określone skomplikowane założenia co do składni, a więc nie tylko sprawdzenie, czy tekst zaczyna się, czy kończy lub czy zawiera określony inny tekst,
B) do wykonania operacji typu Find >> Replace dla skomplikowanych składni tekstowych
C) odnajdywania i zwracania wybranych składni tekstu, np. liczb z danego ciągu tekstowego, albo tego samego ciągu pozbawionego liczb,
D) wyszukiwania adresów e-mail czy adresów internetowych w zbiorze innych wartości tekstowych, itp.

Wykorzystanie techniki RegExp wymagać będzie od nas:
Krok 1) utworzenia referencji do zewnętrznej biblioteki- i o tym napiszę w niniejszym poście,
Krok 2) umiejętności tworzenia wzorców szukanego tekstu- chyba najważniejsza umiejętność w korzystaniu z RegExp, i o tym w kolejnym poście,
Krok 3) umiejętności wykorzystania metod, właściwości, obiektów i kolekcji RegExp lub z RegExp związanych.

Krok 1. 
Wykorzystanie RegExp wymaga od nas ustanowienia referencji do zewnętrznej biblioteki. W tym celu korzystając z jednej z technik wiązań naszą procedurę lub funkcję musimy wzbogacić o następujące alternatywne instrukcje:

wariant A) dla wiązana późnego:

wariant B) dla wiązania wczesnego w pierwszej kolejności musimy dodać referencję do biblioteki Microsoft VBScript Regular Expressions 5.5 (Edytor VBA >> Tools >> References >> odnajdujemy i zaznaczamy wskazaną bibliotekę >> OK). Sam kod będzie miał następującą postać:

Z którego wariantu warto skorzystać? Z praktycznego punktu widzenia- jeżeli tworzony kod nie będzie dystrybuowany zdecydowanie powinniśmy sięgnąć po wariant B, jeżeli jednak tworzymy rozwiązanie dla innych użytkowników powinniśmy zastosować wariant A.
(W sprawie dodatkowych informacji dot. wczesnego i późnego wiązania odsyłam do naszych kursów lub do informacji znajdujących się w sieci.)

środa, 10 lipca 2013

Testowanie zgodności ciągów tekstowych- podstawy- 2/2

Kontynuując tematykę pracy z tekstem i szukania odpowiedzi na pytanie 'czy tekst B spełnia określone kryteria zgodności z tekstem A?' zaprezentuje technikę opartą o wykorzystanie operatora Like.

Ogólna konstrukcja wykorzystania operatora opiera się o schemat:

tekst_A Like wzorzec_B

gdzie w strukturze wzorca dostępne są następujące znaki i bloki specjalne
*        dowolny ciąg tekstu lub ciąg pusty
?        pojedynczy znak- litera lub cyfra
#        pojedyncza cyfra 0 do 9
[zakres]    wskazany zakres znaków, liter, cyfr
[!zakres]   wskazany zakres znaków, liter, cyfr nie uwzględniany w wyszukiwaniu

Spójrzmy jednak na przykłady w celu zrozumienia zastosowania operatora Like.Tym razem naszym badanym tekstem będzie następujący ciąg:
A = "Lorem ipsum dolor 100 sit amet, consectetuer adipiscing elit."

1. Czy w tekście A znajduje się określony fragment tekstu?

Proszę pamiętać, że domyślne porównanie tekstowe odbywa się w sposób binarny, a więc taki, który rozróżnia wielkość liter. Jednym ze sposobów obejścia tego problemu jest zastosowanie następującej techniki:

2. Czy tekst A zaczyna się od małej litery?

3. A może tekst zaczyna się od dowolnej litery (nie cyfry, nie znaku specjalnego)?

Uwaga, nie możemy zapisać zapytania jako "[A-z]" gdy pomiędzy literami A-Z i a-z znajduje się zestaw znaków specjalnych. Można to sprawdzić w następujący sposób (wraz z listą zwracanych wartości):

Jak więc widać powyżej zakresy znaków ujęte w kwadratowych nawiasach [] odpowiadają znakom w kolejności zgodnej z numeracją ANSI.

4. Czy tekst kończy się na literę lub liczbę?

5. A może tekst kończy się wybranym znakiem specjalnym?

6. Sprawdźmy czy tekst zawiera liczbę trzy- lub cztero-cyfrową?

7. Weryfikacja czy tekst jest zdaniem wymaga sprawdzenia, czy zaczyna się od litery wielkiej i kończy jednym ze znaków specjalnych?

8. A na koniec kilka drobnych przykładów, które zwracają wartość TRUE dla analizowanego ciągu tekstowego:

Wszystkich zainteresowanych szerzej zagadnieniem zastosowania opeartora Like odsyłam bezpośrednio do pomocy VBA na stronach Microsoft MSDN:
Like Operator
Comparing Strings by Using Comparison Operators


środa, 3 lipca 2013

Testowanie zgodności ciągów tekstowych- podstawy- 1/2

Niniejsze zagadnienie ma swoje szerokie zastosowanie w całym środowisku VBA i nie dotyczy tylko Excela i Worda lecz wszystkich aplikacji MS Office. Mało tego, spokojnie można stwierdzić, że temat ten jest powszechnym zagadnieniem we wszystkich językach programowania. My spojrzymy jednak na to wyłącznie przez pryzmat VBA.

Ogólnie rzecz ujmując temat na najbliższych kilka postów to poszukiwanie odpowiedzi na pytanie: jak sprawdzić, czy określony ciąg tekstowy spełnia wskazane kryteria? Postaram się przedstawić dostępne techniki lecz nie sposób będzie zaprezentować wszystkich możliwych rozwiązań, schematów czy parametrów. Jest to temat wyjątkowo szeroki i zależny od indywidualnych problemów z jakimi mierzy się każdy programista. Spojrzymy na zagadnienie przez pryzmat rozwiązań prostych (2 posty) oraz możliwość zastosowania `wyrażeń regularnych RegExp` (kilka kolejnych wpisów).

Zaczynamy od rozwiązań najprostszych. Przyjmijmy założenie, że nasz tekst bazowy to:
A = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit."

1. Czy w tekście A znajduje się tekst B? 
W tym wariancie wystarczy wykorzystać funkcję InStr

Należy jednak pamiętać, że porównania tekstowe standardowo dokonywane są binarnie, a więc uwzględniają wielkość liter. Aby pominąć tą niedogodność sięgnijmy po jedno z dwóch poniższych rozwiązań:

2. Czy tekst A zaczyna się od tekstu B?
Tu wystarczy sięgnąć po funkcję Left pamiętając ponownie, że porównanie odbywa się w binarnie.

3. Czy tekst A kończy się tekstem B?
Rozwiązanie będzie podobne jak w punkcie 2 przy czym zastosujemy funkcję Right.

4. Czy określony tekst B znajduje się na określonej pozycji w tekście A?
Poniższy przykład prezentuje rozwiązanie, w którym sprawdzamy czy tekst B znajduje się w tekście A począwszy od 13 litery.