Centrum Skryptów - Systemy operacyjne

Jak usunąć niepotrzebne spacje z pól 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 usunąć niepotrzebne spacje z pól w pliku tekstowym?

Cześć Skrypciarze! Pytanie

Cześć, Skrypciarze! Mam plik tekstowy, w którym każde pole zawiera kilka dodatkowych spacji dołączonych na końcu. Jak usunąć te dodatkowe spacje?

-- GC

Cześć Skrypciarze! Pytanie

Cześć, GC. Chyba nie muszę nikomu mówić, jaki dziś jest dzień, prawda? Zresztą, który by nie był, to Zimowa Olimpiada Skrypciarska 2008 rozpoczyna się już niebawem. Może nie musiałem tego powtarzać, bo w końcu mówi o tym ostatnio cały świat, a przynajmniej skrypciarski światek, ale sprawiło mi to niekłamaną radość. Emocje związane z tym wydarzeniem czuć w powietrzu, pomimo tego, że tu u nas ciągle pada i nie zanosi się, aby miało przestać.

Jeżeli nie jesteście jeszcze pewni swojego uczestnictwa w Olimpiadzie, informacje zamieszczone przez nas na stronie głównej Olimpiady Skrypciarskiej powinny Was ostatecznie przekonać. Bez wątpienia tegoroczna Olimpiada będzie o wiele ciekawsza niż ubiegłoroczna. Ponadto nagrody w tym roku są bardziej zróżnicowane. Każdy znajdzie coś dla siebie:

  • Zdobycie co najmniej 60 punktów w jednej lidze (np. początkujących użytkowników języka Perl) gwarantuje otrzymanie certyfikatu doskonałości.

  • Uczestnictwo w przynajmniej jednych rozgrywkach uprawnia do udziału w losowaniu niezwykłych nagród wymienionych na naszej stronie. Najlepsze jest to, że możecie wziąć udział w losowaniu nawet, jeżeli Wasz skrypt nie zadziała. Czy kiedykolwiek zdobyliście 0 punktów na teście i dostaliście za to nagrodę?

  • Nawet sama przynależność do grupy użytkowników, która (procentowo) ma najwięcej uczestników w Olimpiadzie, zostanie nagrodzona. (Czym? Tego jeszcze nie wiemy, ale pracujemy nad tym.).

  • Reprezentanci kraju, który (procentowo) ma najwięcej uczestników w Olimpiadzie także zostaną nagrodzeni. Nad formą tej nagrody także jeszcze myślimy.

  • Wykorzystanie funkcji powłoki Windows PowerShell 2.0 w jednym ze swoich rozwiązań gwarantuje wygranie koszulki PowerShell z limitowanej edycji.

    To jeszcze nie wszystko.

O samej Olimpiadzie mógłbym opowiadać godzinami, no ale przecież to nie rozwiązałoby problemu GC. Na szczęście mam skrypt, który nam w tym pomoże:

Const ForReading = 1

Const ForWriting = 2



Set objFSO = CreateObject("Scripting.FileSystemObject")



Set objRegEx = CreateObject("VBScript.RegExp")

objRegEx.Global = True   

objRegEx.Pattern = " {2,}"



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



Do Until objFile.AtEndOfStream

    strSearchString = objFile.ReadLine

    strNewString = objRegEx.Replace(strSearchString,"")

    strNewContents = strNewContents & strNewString & vbCrLf

Loop



objFile.Close



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

objFile.Write strNewContents



objFile.Close

Zanim przejdę do dalszej części, zatrzymajmy się na moment w celu pewnych wyjaśnień. GC ma kilka plików zawierających dane podobne do poniższych:

Emailaddr                            |Firstname                        |Lastname

"kmyer@fabrikam.com          "|"Ken                     "|"Myer            "|

"aabudayah@fabrikam.com          "|"Ahmad          "|"Abu Dayah                "|

"htandersen@fabrikam.com          "|"Henriette Thaulow              "|"Andersen"|

GC chce wyeliminować wszystkie spacje znajdujące się na końcu każdego pola. Innymi słowy, po wykonaniu wszystkich czynności, pliki tekstowe mają wyglądać tak:

Emailaddr|Firstname|Lastname

"kmyer@fabrikam.com"|"Ken"|"Myer"|

"aabudayah@fabrikam.com"|"Ahmad"|"Abu Dayah"|

"htandersen@fabrikam.com"|"Henriette Thaulow"|"Andersen"|

Wiecie co? Też o tym pomyślałem: użyjmy metody Replace w celu usunięcia tych spacji. (Czyli zastąpienia każdej z nich „niczym”.). To mogłoby teoretycznie zadziałać. W praktyce: jeżeli usuniemy wszystkie spacje, to nazwisko Henriette Thaulow wyglądałoby tak:

HenrietteThaulow

Nie jest to dokładnie to, co chcemy osiągnąć.

Jeżeli dokładnie przyjrzymy się plikowi tekstowemu zauważymy, że to, co chcemy zrobić, to usunąć wystąpienia dwóch lub więcej pustych, następujących po sobie spacji. Nie możemy usunąć pojedynczych spacji; to spowoduje zamieszanie w nazwiskach. Jeżeli określimy, że chodzi nam o dwie spacje występujące kolejno (plus dowolna liczba spacji następująca po nich), cóż, usuniemy wszystkie spacje, biorąc poprawkę na nazwiska typu Henriette Thaulow oraz Ahmad Abu Dayah. Czy dobrze słyszałem? Ktoś powiedział, że tu przydałoby się wyrażenie regularne? Racja! Użyjemy wyrażenia regularnego w celu wyszukania i zastąpienia dwóch lub większej liczby spacji następujących po sobie. Dzięki temu wyeliminujemy wszystkie niepotrzebne spacje dołączone na końcu każdego pola i zachowamy spacje znajdujące się pomiędzy imieniem i nazwiskiem, jak w przypadku Henriette oraz Thaulow.

W tym celu zaczynamy od zdefiniowania pary stałych, ForReading oraz ForWriting. Wykorzystamy je do otwarcia pliku tekstowego. Po utworzeniu wystąpienia obiektu Scripting.FileSystemObject (potrzebnego podczas pracy z plikiem tekstowym) tworzymy wystąpienie obiektu RegExp skryptu VBScript, czyli obiektu COM, który umożliwi skryptowi VBScript używanie wyrażeń regularnych:

Set objRegEx = CreateObject("VBScript.RegExp")

Kiedy już mamy wystąpienie obiektu RegExp, przypisujemy wartości do dwóch właściwości obiektu. Na początek ustawiamy wartość właściwości Global na True. Dzięki temu obiekt wyrażeń regularnych wie, że chcemy wyszukać wszystkie wystąpienia naszego docelowego tekstu, nie tylko pierwsze. Następnie definiujemy ten tekst docelowy, przypisując następującą wartość do właściwości Pattern:

" {2,}"

Jak widać, mamy tu pustą spację z następującą po niej wartością: {2,}. To oznacza po prostu, że szukamy przynajmniej dwóch kolejnych wystąpień poprzedzającego znaku (czyli właśnie spacji). Przecinek, po którym nic nie następuje, oznacza, że tekst docelowy musi mieć przynajmniej dwie kolejne spacje, ale może mieć dowolną liczbę kolejnych spacji. (Chociaż w takim przypadku wykonanie skryptu mogłoby trochę potrwać.). Innymi słowy, ten model znajdzie dwie kolejne spacje. Znajdzie także trzy kolejne spacje, jedenaście kolejnych spacji, sto czterdzieści siedem kolejnych spacji itd.

Po skonfigurowaniu obiektu wyrażeń regularnych używamy poniższego wiersza kodu w celu otwarcia pliku C:\Scripts\Test.txt do odczytu:

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

Naszym następnym krokiem jest uruchomienie pętli Do Until, która będzie działać, dopóki wartość True właściwości AtEndOfStream się nie zmieni. Oznacza to po prostu, że będziemy odczytywać plik dopóki nic już tam do odczytu nie zostanie. Wewnątrz tej pętli najpierw używamy metody ReadLine w celu odczytania pierwszego wiersza pliku i zachowamy go w zmiennej o nazwie strSearchString:

strSearchString = objFile.ReadLine

Teraz przechodzimy do następującego wiersza kodu:

strNewString = objRegEx.Replace(strSearchString,"")

Używamy tutaj metody Replace obiektu wyrażenia regularnego w celu wyszukania tekstu docelowego i zastąpienia go, no cóż – niczym („”). Co nam to da? Spowoduje to, że wiersz tekstu wyglądający tak:

Emailaddr                            |Firstname                        |Lastname

Zostanie zamieniony na wiersz tekstu wyglądający następująco:

Emailaddr|Firstname|Lastname

W następnym wierszu dodajemy tę nową wartość do zmiennej o nazwie strNewContents. Przy każdym usunięciu dodatkowych spacji z wiersza w pliku tekstowym dodamy ten zmodyfikowany wiersz do zmiennej strNewContents. Po zakończeniu modyfikowania wszystkich wierszy w pliku zastępujemy istniejącą zawartość pliku Test.txt wartością zmiennej strNewContents.

Należy zauważyć, że to podejście ma jedną potencjalną wadę. Przypuśćmy, że pole wygląda tak:

"kmyer@fabrikam.com "|"Ken       "|"Myer     "

Jak widzimy, mamy jedną spację pomiędzy fabrikam.com oraz zamykającym cudzysłowem. Ta spacja prawdopodobnie powinna zostać usunięta. Ponieważ jednak jest to pojedyncza spacja, nasze wyrażenie regularne jej nie odnajdzie. Moglibyśmy spróbować utworzyć bardziej skomplikowane wyrażenie, ale prościej jest użyć naszego prostego wyrażenia regularnego, a następnie wykonać następujący wiersz kodu:

strNewString = Replace(strNewString, " " & Chr(34), Chr(34))

Ten wiersz wyszukuje wszystkie wystąpienia pustej spacji z następującym po niej cudzysłowem (Chr(34)) i zastępuje je po prostu samodzielnym znakiem cudzysłowu. To powinno załatwić sprawę.

W każdym razie po odczytaniu (i zmodyfikowaniu) wszystkich wierszy w pliku tekstowym używamy metody Close i zamykamy plik Test.txt. Następnie od razu wykonujemy poniższy wiersz kodu w celu ponownego otwarcia pliku, tym razem do zapisu:

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

Teraz wystarczy tylko użyć metody Write (oraz zmiennej strNewContents) w celu zastąpienia istniejącej zawartości pliku Test.txt. Następnie wywołujemy metodę Close w celu zamknięcia pliku po raz ostatni i możemy wrócić do naszych ważniejszych spraw.

Może to być na przykład Zimowa Olimpiada Skrypciarska 2008. Pamiętajcie, olimpiada zaczyna się w ten piątek, 15 lutego. Nie musicie pojawiać się już pierwszego dnia, ale nie zapomnijcie, że ostateczny termin dla 1 i 2 konkurencji to środa, 20 lutego godz. 8:00 (czasu Redmond).

 Do początku strony Do początku strony

Centrum Skryptów - Systemy operacyjne