Centrum Skrypciarzy - Systemy Operacyjne

Jak wyszukać w pliku tekstowym ciągi pasujące do określonego wzoru?

Udostępnij na: Facebook

Skrypciarze odpowiadają na Wasze pytania

Cześć Skrypciarze!

Witamy w rubryce TechNet, w której Skrypciarze z firmy Microsoft odpowiadają na częste pytania dotyczące używania skryptów w administracji systemu. Jeśli macie jakieś pytania z tej dziedziny, zachęcamy do wysłania e-maila na adres: scripter@microsoft.com. Nie możemy zagwarantować odpowiedzi na każde otrzymane pytanie, ale staramy się jak możemy.

Jak wyszukać w pliku tekstowym ciągi pasujące do określonego wzoru?

Cześć Skrypciarze! Pytanie

Cześć, Skrypciarze! Jak rozpocząć wyszukiwanie w pliku tekstowym zawierającym identyfikatory produktów i wyszukać tylko wiersze pasujące do określonego wzoru?

-- WT

Cześć Skrypciarze! Odpowiedź

Cześć, WT. Skrypciarz piszący te słowa wdział dziś reklamę samochodu z czujnikiem bicia serca. Działa to w taki sposób, że przed otwarciem drzwi sprawdzamy czujnik, który może wykryć, czy ktoś przypadkiem nie ukrywa się w naszym aucie, czekając na odpowiedni moment, żeby zaatakować. Taka reklama skłania do refleksji na temat sensowności niektórych rozwiązań. Bo przecież zrozumiałe jest zainstalowanie alarmu przeciwpożarowego w domu, gdzie coś może się zapalić, ale czujnik bicia serca w samochodzie?! Jak często zdarza się sytuacja, w której ktoś zaczaja się na kogoś w jego samochodzie? Chyba tylko na filmach. Bynajmniej nie przydarzyło się to jak do tej pory żadnemu Skrypciarzowi. I raczej nie dlatego, że nikt nie chce się zaczaić na Skrypciarza.

Skrypciarz piszący te słowa zastanawia się także nad tym, że pomimo faktu, iż standard naszego życia się podnosi, ludzie są coraz mniej szczęśliwi. Być może dlatego, że pieniądze i rzeczy materialne naprawdę nie dają szczęścia. A może dlatego, że w zawsze w momencie, kiedy wszystko zaczyna wyglądać coraz lepiej, ktoś wymyśla nowy problem, którym trzeba zacząć się przejmować, a następnie stwarza rozwiązanie problemu, o którego istnieniu nikt wcześniej nie wiedział.

W przeciwieństwie do takich osobników, Skrypciarze stwarzają rozwiązania problemów istniejących naprawdę. (W sumie jesteśmy odpowiedzialni za wiele z nich, ale to już inna historia.) Na przykład, są tacy, którzy potrzebują uzyskać listę określonych produktów z pliku tekstowego. Jak mają to zrobić? A tak:

Const ForReading = 1



Set objRegEx = CreateObject("VBScript.RegExp")

objRegEx.Pattern = "^[1-9]...GRP"



Set objFSO = CreateObject("Scripting.FileSystemObject")

Set objFile = objFSO.OpenTextFile("C:\Scripts\Test.txt", ForReading)



Do Until objFile.AtEndOfStream

    strSearchString = objFile.ReadLine

    Set colMatches = objRegEx.Execute(strSearchString)  

    If colMatches.Count > 0 Then

        For Each strMatch in colMatches   

            Wscript.Echo strSearchString 

        Next

    End If

Loop



objFile.Close

Zanim wyjaśnimy działanie skryptu, powinniśmy zauważyć, że zgodnie z opisem WT, mamy tekst podobny do poniższego:

1XXXGRPABCEFG

2YYYGRPDEF

AZZZGRPDEF

RTRRABCGRPRTY

YTHJABCPBCOP

Szukamy tylko takich rekordów (wierszy w pliku tekstowym), które spełniają następujące kryteria:

  • Pierwszy znak to numer z przedziału od 1 do 9. Ten znak wskazuje na określony typ produktu
  • Drugi, trzeci i czwarty znak to – nieważne. Akurat te znaki nas nie interesują.
  • Znak piąty, szósty i siódmy to GRP. Określają one różne grupy produktów.
  • Pozostałe znaki to – cóż, to też nas nie obchodzi.

Na podstawie tych kryteriów stwierdzamy, że nie interesują nas tylko dwa pierwsze wiersze w pliku tekstowym: obydwa zaczynają się od numeru, a jako piąty, szósty i siódmy znak mają GRP. Oczywiście wiersz trzeci także posiada GRP w wyznaczonym miejscu, jednak nie rozpoczyna się od numeru. Jeżeli chodzi o wiersz czwarty i piąty, to im mniej o nich powiemy, tym lepiej.

Jak więc odnajdziemy pożądane miejsca w pliku? Dla nas najprostszym sposobem było zastosowanie wyrażenia regularnego; w końcu szukamy określonego wzoru (numeru po którym następują trzy znaki, po których z kolei następuje GRP) a wyrażenia regularne idealnie się nadają do wyszukiwania wzorów (w przeciwieństwie do wyszukiwania określonych słów i zwrotów). A czy nasze wyrażenie zadziała? Zaraz się przekonamy.

Skrypt zaczyna od utworzenia stałej o nazwie ForReading i nadania jej wartości 1; wykorzystamy tę stałą podczas otwierania naszego pliku tekstowego do odczytu. Następnie zastosujemy poniższe dwa wiersze kodu do utworzenia wystąpienia obiektu VBScript.RegExp i określimy wzór (Pattern) naszego wyszukiwania:

Set objRegEx = CreateObject("VBScript.RegExp")

objRegEx.Pattern = "^[1-9]...GRP"

Chyba nie ma potrzeby wspominać, że Pattern jest kluczem do tego, aby nasz skrypt zadział; z tego powodu poświęcimy trochę czasu na objaśnienie różnych składników tego wyrażenia regularnego. Na początek znak ^.Przekazuje on skryptowi informację, że wzór musi zostać odnaleziony na początku tekstu wyszukiwania; zapobiegnie to oznaczeniu wartości podobnej do poniższej jako elementu pasującego:

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1XXXGRP

Jak widać, wiersz ten zawiera interesujący nas wzór, jednak występuje on na samym końcu ciągu, a nie na początku Dlatego tez się nie liczy, przynajmniej nie dla tego skryptu.

Następnie mamy składnik: [1-9]. Mówi to po prostu o tym, że następnym znakiem naszego wzoru musi być cyfra z przedziału od 1 do 9 (kwadratowe nawiasy wskazują zakres akceptowalnych wartości). Innymi słowy, żeby pasować, ciąg musi rozpoczynać się od cyfry z przedziału od 1 do 9. Jak dotąd całkiem proste, prawda? O tak.

Teraz mamy trzy kropki: …. Co one oznaczają? W wyrażeniu regularnym kropka oznacza jakikolwiek znak (z wyjątkiem znaku nowej linii). Używamy tutaj trzech kropek w celu zaznaczenia, że aby spełnić kryteria naszego wzoru, ciąg musi zawierać trzy znaki występujące po początkowej cyfrze. Jakie trzy znaki? To już nie jest takie ważne.

Na koniec, mamy kod grupy produktów: GRP. Potrzebne nam są trzy znaki występujące po początkowej cyfrze. Po tych trzech znakach potrzebne nam są litery GRP, w takiej właśnie kolejności. Każdy ciąg, który pasuje do całego wzoru zostanie uznany jako element pasujący; z kolei każdy ciąg, który nie pasuje do całego wzoru nie zostanie uznany za element pasujący.

Po określeniu wzoru, stosujemy poniższe dwa wiersze kodu w celu utworzenia wystąpienia obiektu Scripting.FileSystem i otwarcia pliku tekstowego C:\Scripts\Test.txt do odczytu:

Set objFSO = CreateObject("Scripting.FileSystemObject")

Set objFile = objFSO.OpenTextFile("C:\Scripts\Test.txt", ForReading)

Teraz zabawa rozpoczęła się na dobre. Następnie uruchamiamy pętlę Do Until, która będzie działać dopóki nie odczytamy każdego jednego wiersza w pliku tekstowym (a dokładnie, dopóki właściwość AtEndOfStream nie osiągnie wartości True). Wewnątrz tej pętli stosujemy metodę ReadLine do odczytania pierwszego wiersza w pliku tekstowym i zachowania go w zmiennej o nazwie strSearchString. Tu potrzeby nam jest następujący wiersz kodu:

Set colMatches = objRegEx.Execute(strSearchString)

Używamy metody Execute w celu określenia, czy nasz wzór wyrażenia regularnego znajduje się w zmiennej strSearchString. Jeżeli tak, ta informacja zostanie zwrócona jako kolekcja o nazwie colMatches. Jeżeli nie, no cóż, colMatches będzie kolekcją zawierającą 0 pozycji. Mając to na uwadze, sprawdzamy teraz jedynie, czy kolekcja colMatches zawiera jakieś pozycje. Jeżeli tak, wywołujemy echo wartości zmiennej strSearchString. (Dlaczego? Ponieważ jest to wartość pasująca do naszego wzoru.) Jeżeli nie, uruchamiamy pętlę i powtarzamy proces dla następnego wiersza w pliku tekstowym. Wszystko to za pomocą następującego wiersza kodu:

If colMatches.Count > 0 Then

    For Each strMatch in colMatches   

        Wscript.Echo strSearchString 

    Next

End If

Po zakończeniu, zamykamy plik tekstowy Test.txt, siadamy wygodnie i podziwiamy efekty naszej pracy:

1ABCGRPABCEFG

2DEFGRPDEF

Pięknie.

 Do początku strony Do początku strony

Centrum Skrypciarzy - Systemy Operacyjne