Centrum skryptów - Systemy operacyjne

Jak usunąć powtarzające się wpisy w pliku rozdzielanym tabulatorami?

Udostępnij na: Facebook

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.

  1. Wasz
  2. ulubiony
  3. Skrypciarz
  4. błyskawicznie
  5. odpowiadający
  6. na
  7. wszystkie
  8. 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 Do początku strony

Centrum skryptów - Systemy operacyjne