Centrum Skrypciarzy - Systemy Operacyjne

Jak policzyć liczbę słów w pliku tekstowym?

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 policzyć liczbę słów w pliku tekstowym?

Cześć Skrypciarze! Pytanie

Cześć, Skrypciarze! Jak policzyć liczbę słów w pliku tekstowym?

-- LA

Cześć Skrypciarze! Odpowiedź

Cześć, LA. Wiesz co, to jedno z pytań, przy których Skrypciarze chcieli być za sprytni. Po pierwsze, piszemy to w piątek, a w piątki zawsze chcemy iść po linii najmniejszego oporu. Po drugie, wczoraj właśnie rozmawialiśmy o liczeniu słów, więc zdawało się nam, że wiemy co nieco na ten temat. A że pytanie wyglądało na łatwe, uznaliśmy, że doskonale się nadaje na piątek.

Zorientowaliśmy się, że nie będzie łatwo, kiedy tylko zabraliśmy się za pisanie skryptu. Istnieje kilka możliwych punktów wyjścia do rozwiązania problemu. Na przykład, bez problemu można liczyć słowa (j.ang.) za pomocą programu Microsoft Word, więc z początku chcieliśmy właśnie tak się za to zabrać – używając Microsoft Word. Ale zaraz uznaliśmy, że to jak strzelać z armaty do wróbli, no i nie chcieliśmy, aby wyszło na to, że w celu policzenia słów w pliku tekstowym trzeba kupić pakiet Microsoft Office. (Mimo, że z pewnością moglibyśmy w takim wypadku liczyć na udział w zyskach zespołu Office.) Pomyśleliśmy więc, że to świetny przykład, by przedstawić wyrażenia regularne (j.ang.). Ale kiedy zaczęliśmy się w to wgłębiać, zaraz rozbolały nas głowy.

No i potem wymyśliliśmy takie oto proste i eleganckie rozwiązanie:

Const ForReading = 1



Set objFSO = CreateObject("Scripting.FileSystemObject")

Set objFile = objFSO.OpenTextFile("c:\scripts\test.txt", ForReading)



strText = objFile.ReadAll

objFile.Close



arrWords = Split(strText, " ")

Wscript.Echo Ubound(arrWords) + 1

Po prostu pięknie: otwieramy tu plik tekstowy C:\Scripts\Test.txt i zapisujemy jego całą zawartość w zmiennej o nazwie strText. Następnie używamy funkcji Split, za pomocą której rozdzielamy tekst i tworzymy tablicę, używając spacji jako delimiterów (przy założeniu, że spacje pojawiają się tylko między słowami.) Teraz mamy tablicę o nazwie arrWords, w której każdy element oznacza jedno słowo, wystarczy więc, że wywołamy echo wartości Ubound (górna granica) tej tablicy i powiększymy ją o jeden. Skąd to powiększenie? Otóż wartość Ubound jest mniejsza od liczby elementów tablicy o jeden.

Zadziałało – mniej-więcej. Jak się okazało, użyty przez nas plik tekstowy miał dodatkowe spacje, użyte do formatowania:

Name                                        Date

Ken Myer                                    3/30/2006

Pilar Ackerman                              3/31/2006

To był problem – każda dodatkowa spacja liczyła się jak osobne słowo. Tak więc ostatecznie liczba słów była większa, niż byśmy chcieli.

Skrypt w wersji 2.0:

Const ForReading = 1



Set objFSO = CreateObject("Scripting.FileSystemObject")

Set objFile = objFSO.OpenTextFile("c:\scripts\test.txt", ForReading)



strText = objFile.ReadAll

objFile.Close



arrWords = Split(strText, " ")



For Each strWord in arrWords

    If Len(strWord) > 0 Then

        i = i + 1

    End If

Next



Wscript.Echo i

Jak widać, tym razem nie wywołujemy echa wartości Ubound. Zamiast tego uruchamiamy pętlę For Each, która przechodzi przez wszystkie elementy znajdujące się w tablicy. Wewnątrz pętli używamy funkcji Len, która określa liczbę znaków w poszczególnych elementach. Jeśli liczba ta jest równa 0, zakładamy, że chodzi o dodatkową, niepotrzebną spację. Element taki po prostu przeskakujemy – rzadko się zdarza, aby jakieś słowo miało zero znaków. Jeśli długość jest większa niż zero, zwiększamy zmienną licznika o 1:

i = i + 1

Po przejściu pętli przez całą tablicę wywołujemy echo naszej zmiennej licznika:

Wscript.Echo i

Teraz już dużo lepiej, ale podana liczba słów ciągle wydaje się za duża. Po paru chwilach pomyślunku doszliśmy, o co chodzi. Załóżmy, że mamy takie zdanie:

Two plus two = four

Wydawałoby się, że są tu cztery słowa. Jednak skrypt uparcie twierdził, że jest ich pięć:

Two

plus

two

=

four.

Okazuje się, że skrypt liczy znak równości (=) jako słowo. Zdarzały się też inne nadmiarowe słowa. Na przykład taki ciąg jak poniżej to dla skryptu trzy słowa:

. . .

Oj.

Nie byliśmy z tego zadowoleni, więc zmodyfikowaliśmy nasz skrypt, tym razem kilkakrotnie używając funkcji Replace i zastępując znaki w rodzaju kropek czy wykrzykników zwykłymi spacjami. Oto wersja 2.1:

Const ForReading = 1



Set objFSO = CreateObject("Scripting.FileSystemObject")

Set objFile = objFSO.OpenTextFile("c:\scripts\test.txt", ForReading)



strText = objFile.ReadAll



strText = Replace(strText, ",", " ")

strText = Replace(strText, ".", " ")

strText = Replace(strText, "!", " ")

strText = Replace(strText, "?", " ")

strText = Replace(strText, ">", " ")

strText = Replace(strText, "<", " ")

strText = Replace(strText, "&", " ")

strText = Replace(strText, "*", " ")

strText = Replace(strText, "=", " ")



strText = Replace(strText, vbCrLf, " ")



objFile.Close



arrWords = Split(strText, " ")



For Each strWord in arrWords

    If Len(strWord) > 0 Then

        i = i + 1

    End If

Next



Wscript.Echo i

Teraz było już dobrze. Podobnie jak w wypadku poprzednich skryptów, zaczynamy od zdefiniowania stałej o nazwie ForReading; informuje ona FileSystemObject, że chcemy odczytać plik tekstowy (a nie np. zmodyfikować go). Następnie tworzymy wystąpienie FileSystemObject i za pomocą metody OpenTextFile otwieramy plik C:\Scripts\Test.txt. Kiedy uruchomimy FileSystemObject, używamy metody ReadAll do odczytania całego pliku i przechowania go w zmiennej strText:

strText = objFile.ReadAll

Następnie uruchamiamy serię funkcji Replace, które zastępują różne znaki w zmiennej strText. (Zwróćmy uwagę, że nie ruszamy tu właściwego pliku, a jedynie jego kopię zapisaną w pamięci.) I tak, poniższy wiersz kodu zastępuje wszystkie przecinki spacjami:

strText = Replace(strText, ",", " ")

Każdy użytkownik może samodzielnie wybrać znaki do zastąpienia spacjami. Jeśli np. chcemy, aby znaki równości i plusy liczyły się jako słowa, możemy usunąć ze skryptu odpowiednie wiersze. Albo usunąć wszystkie funkcje Replace.

Stop. Jedną z nich trzeba jednak zostawić. Załóżmy że plik wygląda tak:

A

B

C

D

E

Ile mamy tu słów? Na oko 5, ale według skryptu – tylko jedno. Dlaczego? Otóż rozdzielamy skrypt w miejscach spacji. Jednak w powyższym pliku nie ma ani jednej, mamy tylko znaki powrotu karetki na końcu każdego wiersza. Dlatego też utworzona w ten sposób tablica ma tylko jeden element.

Jak to rozwiązać? Żaden problem – po prostu zastępujemy wszystkie powroty karetki (vbCrLf) spacjami:

strText = Replace(strText, vbCrLf, " ")

Teraz, kiedy poszczególne znaki są rozdzielone spacjami, skrypt prawidłowo zgłasza, że słów jest pięć.

Przy czym to myśmy byli? Ach tak. Po zamknięciu pliku wywołujemy funkcję Split, która rozdziela strText, tworząc tablicę. Następnie używamy takiej samej jak poprzednio pętli For Each, która liczy słowa w tablicy (odpowiadające słowom w pliku tekstowym), opuszczając dodatkowe spacje. Następnie wywołujemy echo zmiennej licznika – i gotowe.

Przynajmniej jeśli chodzi o nasze wymagania. Dokładność liczenia jest kwestią nieco subiektywną – weźmy na przykład taki wiersz:

2+2=4

Czy jest tu 5 słów (2, +, 2, =, and 4)? A może tylko 3: 2, 2 oraz 4. A może tylko jedno: 2+2=4. (Microsoft Word uznaje to za jedno słowo). Takie decyzje każdy użytkownik musi podjąć samodzielnie. A jeśli chodzi o nas, mamy nauczkę – kolejne „proste” pytanie po prostu pominiemy!

 Do początku strony Do początku strony

Centrum Skrypciarzy - Systemy Operacyjne