Jak usunąć powtarzające się wpisy w pliku rozdzielanym tabulatorami?
Skrypciarze odpowiadają na Wasze pytania
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ąć powtarzające się wpisy w pliku rozdzielanym tabulatorami?
Cześć, Skrypciarze! Jak usunąć powtarzające się wpisy w pliku rozdzielanym tabulatorami?
-- ST
Cześć, ST. Zanim zacznę, mam do Was pytanie: czy słyszeliście może o książce pod tytułem Not Quite What I Was Planning: Six-Word Memoirs by Writers Famous and Obscure (j. ang.)? Jest to interesująca książeczka, w której redaktorzy magazynu SMITH (j. ang.) poprosili różne osoby, żeby podsumowali swoje życie w sześciu słowach. Oto kilka przykładowych odpowiedzi:
- Znalazłam prawdziwą miłość, wyszłam za innego.
- Dalej parzę kawę dla dwóch osób.
- Dobrze urodzony, niestety zszedł na psy.
- Dużo szczęścia w życiu, mało podróży.
Jedna jest szczególnie ciekawa:
- Grzyby. Klauni. Buławy. Pięć. Peruka. Strzecha.
Lepiej się nie zastanawiać, co to wszystko znaczy.
W każdym razie pomyślałem sobie, że fajnie by było zastosować ten sposób w rubryce Cześć Skrypciarze! W końcu, gdybym mógł odpowiadać na pytania używając sześciu słów, zaoszczędziłbym mnóstwo czasu i wysiłku. Niestety szybko zrezygnowałem z tego pomysłu, nie mogę nawet powiedzieć nic na swój temat używając tylko sześciu słów.
- Wasz
- ulubiony
- Skrypciarz
- błyskawicznie
- odpowiadający
- na
- wszystkie
- pytania
Może zabrzmiało to mało skromnie, ale przecież to cała prawda o mnie.
Ale jest jeszcze jedna możliwość: „Oto ten kod. życzę Wam powodzenia”. Nie wiem tylko, czy takie wyjaśnienie działania skryptu by Wam wystarczyło. Obawiam się, że nie. |
Dlatego też zadecydowałem pójść na kompromis: część dzisiejszego skryptu opiszę we fragmentach zawierających sześć słów i umieszczę je w kwadratowych nawiasach. Reszta rubryki pozostanie w tradycyjnym stylu. [Tradycyjny styl, co w tym złego?]
Zatem zaczynamy. Jak usunąć powtarzające się wpisy w pliku rozdzielanym tabulatorami? Odpowiedź kryje się w tym oto skrypcie
(naprawdę chciałem to powiedzieć używając sześciu słów, ale tak się nie da):
Const ForReading = 1
Const ForWriting = 2
Set objDictionary = CreateObject("Scripting.Dictionary")
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("C:\Scripts\Test.txt", ForReading)
strContents = objFile.ReadAll
objFile.Close
strContents = Replace(strContents, vbCrLf, vbTab)
arrContents = Split(strContents, vbTab)
For Each strItem in arrContents
If Not objDictionary.Exists(strItem) Then
objDictionary.Add strItem, strItem
End If
Next
i = 1
For Each strKey in objDictionary.Keys
If i < 3 Then
strNewContents = strNewContents & strKey & vbTab
i = i + 1
Else
strNewContents = strNewContents & strKey & vbCrLf
i = 1
End If
Next
Set objFile = objFSO.OpenTextFile("C:\Scripts\Test.txt", ForWriting)
objFile.Write strNewContents
objFile.Close
Jak widzimy, nasz skrypt zaczyna się od zdefiniowania pary stałych, ForReading oraz ForWriting, które wykorzystamy podczas otwierania pliku tekstowego. [Dwie stałe. Plik otwierany dwa razy.] Po zdefiniowaniu tych stałych tworzymy wystąpienie obiektów Scripting.Dictionary oraz Scripting.FileSystemObject, a następnie używamy poniższego wiersza kodu w celu otarcia pliku C:\Scripts\Test.txt do odczytu:
Set objFile = objFSO.OpenTextFile("C:\Scripts\Test.txt", ForReading)
Od razu po otwarciu pliku używamy metody ReadAll w celu odczytania całej zawartości pliku i zachowujemy te informacje w zmiennej o nazwie strContents. Następnie wywołujemy metodę Close i zamykamy (tymczasowo) plik Test.txt. [Mamy już plik. Co teraz robimy?]
Zanim zrobimy cokolwiek innego, przyjrzyjmy się plikowi, który właśnie otwarliśmy (oraz, nieprzypadkowo, wartości zmiennej strContents):
Set objFile = objFSO.OpenTextFile("C:\Scripts\Test.txt", ForReading)
Jak widzimy, to po prostu trzykolumnowa lista różnych owoców. Widzimy także, że wiele z tych owoców się powtarza. Na przykład mamy trzy wystąpienia słowa Apple (jabłko) i dwa słowa Peach (brzoskwinia). [Trzy kolumny. Różne owoce. Powtarzające się.] Chcemy teraz wyeliminować wszystkie powtarzające się wpisy. Innymi słowy, chcemy, aby nasza trzykolumnowa lista wyglądała tak:
Apple Banana Cherry
Date Fig Lemon
Orange Peach Pear
Dobre pytanie: jak to zrobimy? Cóż, na początek potrzebna nam lista słów, z którą łatwiej pracować. Tak naprawdę potrzebujemy listy słów składającej się z jednej kolumny zamiast tej z trzema kolumnami. [Trzy kolumny to już za dużo.] W celu utworzenia jednej listy słów zastąpimy wszelkie wystąpienia znaku powrotu karetki (vbCrLf) znakiem tabulatora (vbTab):
strContents = Replace(strContents, vbCrLf, vbTab)
W ten sposób otrzymamy jeden wiersz zawierający wszystkie słowa, przy czym każde z nich będzie oddzielone tabulatorem. Innymi słowy otrzymamy coś takiego (pamiętajcie, że ten wiersz mógłby się bez końca zawijać):
Apple Apple Apple Banana Cherry Cherry
[Jeden samotny banan. Życie jest niesprawiedliwe.]
Może się wydawać, że robimy postępy. Możecie wierzyc lub nie, ale naprawdę tak jest. [Wielkie nieba. Skrypciarze robią czasem postępy.] Teraz, gdy wszystkie słowa są oddzielone tabulatorami, możemy zastosować funkcję Split w celu podzielenia zawartości zmiennej strContents używając znaku tabulatora jako delimitera:
arrContents = Split(strContents, vbTab)
Dlaczego mielibyśmy to zrobić? Ponieważ dzięki temu otrzymujemy tablicę o nazwie arrContents, zawierającą następujące elementy:
Apple
Apple
Apple
Banana
Cherry
Cherry
Cherry
Date
Fig
Fig
Lemon
Orange
Orange
Peach
Peach
Pear
Pear
Pear
Kiedy już mamy taką tablicę, wyeliminowanie powtarzających się elementów jest całkiem proste. W tym celu najpierw uruchamiamy pętlę For Each, która przejdzie przez wszystkie elementy w tablicy arrContents. [For Each: na każdego przyjdzie czas.]
For Each strItem in arrContents
Wewnątrz tej pętli za pomocą metody Exists sprawdzamy, czy pierwszy element w tablicy istnieje w obiekcje Dictionary [Widzicie? Nigdy o nim nie zapominamy.]:
If Not objDictionary.Exists(strItem) Then
Jeśli ten element istnieje w obiekcie Dictionary (słownik), oznacza to, że jest to element powtarzający się. W takim przypadku wracamy na początek pętli i powtarzamy proces dla następnego elementu w tablicy. Jeśli ten element nie istnieje w Dictionary, używamy poniższego wiersza kodu w celu dodania tego elementu do obiektu, używając tego słowa jako klucza i elementu obiektu Dictionary:
objDictionary.Add strItem, strItem
PO wykonaniu wszystkich czynności obiekt Dictionary powinien zawierać następujące klucze (i elementy):
Apple
Banana
Cherry
Date
Fig
Lemon
Orange
Peach
Pear
Całkiem nieźle, tylko teraz musimy zamienić tę jednokolumnową listę z powrotem na listę składającą się z trzech kolumn: [Niekiedy trzy jest lepsze niż dwa.] W tym celu najpierw ustawiamy wartość zmiennej licznika o nazwie i jako 1:
i = 1
Następnie uruchamiamy drugą pętle For Each, która ma za zadanie przejść przez wszystkie klucze w obiekcie Dictionary [Pętli nigdy nie jest za dużo.]:
For Each strKey in objDictionary.Keys
Ponieważ chcemy uzyskać listę zawierającą trzy kolumny, wewnątrz tej pętli najpierw sprawdzamy, czy wartość naszej zmiennej licznika i jest mniejsza niż 3. Jeżeli tak jest, wykonujemy poniższy fragment kodu:
strNewContents = strNewContents & strKey & vbTab
i = i + 1
Jak widać, nie ma w tym zupełnie nic skomplikowanego. [U Skrypciarzy: proste myślenie, proste skrypty.] W wierszu 1 przypisujemy wartość do zmiennej o nazwie strNewContents, a konkretniej przypisujemy tej zmiennej istniejącą wartość zmiennej strContents plus wartość klucza obiektu Dictionary plus znak tabulatora. W wierszu 2 zwiększamy wartość zmiennej licznika o 1. Co to oznacza? To oznacza, że po trzech przejściach pętli zmienna strContents będzie miała następującą wartość:
Apple Banana Cherry
Co się stanie podczas następnego przejścia pętli? Cóż, podczas następnego przejścia pętli zmienna licznika i będzie miała wartość 3. To oznacza, że teraz wykonamy poniższe dwa wiersze kodu:
strNewContents = strNewContents & strKey & vbCrLf
i = 1
Zauważyliście różnicę? Za pomocą tego fragmentu kodu zamiast dodawać znak tabulatora na końcu ciągu dodajemy znak powrotu karetki. Poza tym, resetujemy także wartość zmiennej licznika i do 1. Te dwie rzeczy zagwarantują nam, że następny element na naszej liście (element czwarty) pojawi się w pierwszej kolumnie w wierszu 2 zamiast w czwartej kolumnie w wierszu 1.
Po przeformatowaniu zmiennej strNewContents otwieramy ponownie plik Test.txt, tym razem do odczytu:
Set objFile = objFSO.OpenTextFile("C:\Scripts\Test.txt", ForWriting)
Teraz używamy metody Write w celu zastąpienia istniejącej zawartości pliku Test.txt wartością zmiennej strNewContents, zamykamy plik za pomocą metody Close po raz ostatni, tym razem na dobre. [Plik zamknięty? Gra chyba się skończyła.]
I to już wszystko na dziś, ST. Jeśli macie jakieś pytanie, koniecznie dajcie nam znać. Jeśli potraficie opisać Centrum skryptów i Skrypciarzy za pomocą sześciu słów, też dajcie nam znać. Opublikujemy je w następnym artykule.
Do początku strony |