2009-05-31 10 views
8

Mam ogromny plik tekstowy z 25k lines.Inside tego pliku tekstowego każdy wiersz zaczyna się od „1 \ t (LineNumber)”Czy istnieje opcja "przejdź do linii" w TextReader/StreamReader?

Przykład:

1 1 ITEM_ETC_GOLD_01 골드(소) xxx xxx xxx_TT_DESC 0 0 3 3 5 0 180000 3 0 1 0 0 255 1 1 0 0 0 0 0 0 0 0 0 0 -1 0 -1 0 -1 0 -1 0 -1 0 0 0 0 0 0 0 100 0 0 0 xxx item\etc\drop_ch_money_small.bsr xxx xxx xxx 0 2 0 0 1 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 0 0 0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1 표현할 골드의 양(param1이상) -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx 0 0 
1 2 ITEM_ETC_GOLD_02 골드(중) xxx xxx xxx_TT_DESC 0 0 3 3 5 0 180000 3 0 1 0 0 255 1 1 0 0 0 0 0 0 0 0 0 0 -1 0 -1 0 -1 0 -1 0 -1 0 0 0 0 0 0 0 100 0 0 0 xxx item\etc\drop_ch_money_normal.bsr xxx xxx xxx 0 2 0 0 1 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 0 0 0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000 표현할 골드의 양(param1이상) -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx 0 0 
1 3 ITEM_ETC_GOLD_03 골드(대) xxx xxx xxx_TT_DESC 0 0 3 3 5 0 180000 3 0 1 0 0 255 1 1 0 0 0 0 0 0 0 0 0 0 -1 0 -1 0 -1 0 -1 0 -1 0 0 0 0 0 0 0 100 0 0 0 xxx item\etc\drop_ch_money_large.bsr xxx xxx xxx 0 2 0 0 1 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 0 0 0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 10000 표현할 골드의 양(param1이상) -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx 0 0 
1 4 ITEM_ETC_HP_POTION_01 HP 회복 약초 xxx SN_ITEM_ETC_HP_POTION_01 SN_ITEM_ETC_HP_POTION_01_TT_DESC 0 0 3 3 1 1 180000 3 0 1 1 1 255 3 1 0 0 1 0 60 0 0 0 1 21 -1 0 -1 0 -1 0 -1 0 -1 0 0 0 0 0 0 0 100 0 0 0 xxx item\etc\drop_ch_bag.bsr item\etc\hp_potion_01.ddj xxx xxx 50 2 0 0 1 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 0 0 0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 120 HP회복양 0 HP회복양(%) 0 MP회복양 0 MP회복양(%) -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx 0 0 
1 5 ITEM_ETC_HP_POTION_02 HP 회복약 (소) xxx SN_ITEM_ETC_HP_POTION_02 SN_ITEM_ETC_HP_POTION_02_TT_DESC 0 0 3 3 1 1 180000 3 0 1 1 1 255 3 1 0 0 1 0 110 0 0 0 2 39 -1 0 -1 0 -1 0 -1 0 -1 0 0 0 0 0 0 0 100 0 0 0 xxx item\etc\drop_ch_bag.bsr item\etc\hp_potion_02.ddj xxx xxx 50 2 0 0 2 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 0 0 0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 220 HP회복양 0 HP회복양(%) 0 MP회복양 0 MP회복양(%) -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx 0 0 
1 6 ITEM_ETC_HP_POTION_03 HP 회복약 (중) xxx SN_ITEM_ETC_HP_POTION_03 SN_ITEM_ETC_HP_POTION_03_TT_DESC 0 0 3 3 1 1 180000 3 0 1 1 1 255 3 1 0 0 1 0 200 0 0 0 4 70 -1 0 -1 0 -1 0 -1 0 -1 0 0 0 0 0 0 0 100 0 0 0 xxx item\etc\drop_ch_bag.bsr item\etc\hp_potion_03.ddj xxx xxx 50 2 0 0 3 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 0 0 0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 370 HP회복양 0 HP회복양(%) 0 MP회복양 0 MP회복양(%) -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx 0 0 
1 7 ITEM_ETC_HP_POTION_04 HP 회복약 (대) xxx SN_ITEM_ETC_HP_POTION_04 SN_ITEM_ETC_HP_POTION_04_TT_DESC 0 0 3 3 1 1 180000 3 0 1 1 1 255 3 1 0 0 1 0 400 0 0 0 7 140 -1 0 -1 0 -1 0 -1 0 -1 0 0 0 0 0 0 0 100 0 0 0 xxx item\etc\drop_ch_bag.bsr item\etc\hp_potion_04.ddj xxx xxx 50 2 0 0 4 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 0 0 0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 570 HP회복양 0 HP회복양(%) 0 MP회복양 0 MP회복양(%) -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx 0 0 

Pytanie: W jaki sposób można odczytać bezpośrednio na przykład, linia 5?

Odpowiedz

10

Można używać LineReader klasę (albo jeden w MiscUtil lub simple version here) w celu wdrożenia IEnumerable<string> a następnie użyć LINQ:

string line5 = new LineReader(file).Skip(4).First(); 

Zakłada .NET 3.5, co prawda. W przeciwnym razie otwórz numer TextReader (np. Z File.OpenText) i po prostu cztery razy zadzwoń pod numer ReadLine(), aby pominąć linie, których nie chcesz, a następnie jeszcze raz, aby przeczytać piątą linię.

Nie ma możliwości "skrócenia" tego, chyba że wiesz dokładnie, ile bajtów znajduje się w każdej linii.

+0

Pod maską, która wciąż czyta linię po linii, aż osiągnie żądaną linię. Czy istnieje sposób, aby przejść bezpośrednio do linii 5? – BFree

+2

W jaki sposób strumień powinien znać przesunięcie bajtowe linii 5 przed czasem, BFree? –

+0

Czy istnieje powód, dla którego LineReader nie ma przeciążenia konstruktora Stream, tylko Func ? –

3

Jeśli masz do czynienia z formatem danych o stałej szerokości (tzn. Znasz wszystkie linie o tej samej długości), możesz pomnożyć długość z żądanym numerem linii i użyć Stream.Seek, aby znaleźć punkt początkowy n-tej linii.

Jeśli linie nie mają ustalonej długości, musisz znaleźć odpowiednią liczbę podziałów linii, aż znajdziesz się na początku żądanej linii. To byłoby najłatwiej zrobić z StreamReader.ReadLine. (Możesz zrobić metodę rozszerzenia, aby plik en IEnumerable <string>, jak sugeruje Jon Skeet - to dostarczyłoby ci ładniejszej składni, ale pod maską będziesz korzystał ReadLine).

Jeśli wydajność jest problemem, może to być (nieco) bardziej efektywne skanowania <CR> <LF> sekwencji bajt w plikach, stosując metodę stream.Read. Nie testowałem tego; ale StreamReader oczywiście musi wykonać trochę pracy, aby skonstruować ciąg z sekwencji bajtów - jeśli nie dbasz o pierwsze linie, ta praca może zostać zapisana, więc teoretycznie powinieneś być w stanie zrobić metodę skanowania, która działa lepiej . To jednak dla ciebie dużo więcej pracy.

+0

Linie nie mają ustalonej długości, ale można znaleźć długość każdej linii. Jednakże zajmie to trochę czasu, czytając każdą linię dla tak dużych plików z 25 000 linii. –

+0

Jeśli idziesz tylko do piątej linii, nie musisz czytać wszystkich linii ... –

+0

Jeśli nie znasz długości każdej linii przed czasem, nie ma innej możliwości niż przejście przez każdą linię w ten czy inny sposób, aby znaleźć konkretną linię. Nie ma magicznego skrótu. Jeśli jest to plik, do którego są dołączane, i musisz przetworzyć nowe dane, możesz zapisać ostatnie przesunięcie bajtowe między odczytami i zacząć od tego miejsca podczas następnego odczytu. – driis

3

Nie można przeskoczyć bezpośrednio do wiersza w pliku tekstowym, chyba że każda linia ma stałą szerokość, a używasz kodowania o stałej szerokości (tj. Nie jest to kodowanie UTF-8 - które jest obecnie najbardziej popularne).

Jedynym sposobem, aby to zrobić, jest czytanie linii i odrzucanie tych, których nie chcesz.

Alternatywnie możesz umieścić indeks na górze pliku (lub w pliku zewnętrznym), który mówi mu (na przykład), że linia 1000 zaczyna się od przesunięcia bajtu [x], linia 2000 zaczyna od przesunięcia bajtu [y] ] itd. Następnie użyj .Position lub .Seek() na FileStream, aby przejść do najbliższego zindeksowanego punktu i idź do przodu.

Zakładając najprostsze podejście (bez indeksu), kod w przykładzie Jona powinien działać poprawnie. Jeśli nie chcesz, LINQ, można wbić się coś podobnego w .NET 2.0 + C# 2.0:

// to read multiple lines in a block 
public static IEnumerable<string> ReadLines(
     string path, int lineIndex, int count) { 
    if (string.IsNullOrEmpty(path)) throw new ArgumentNullException("path"); 
    if (lineIndex < 0) throw new ArgumentOutOfRangeException("lineIndex"); 
    if (count < 0) throw new ArgumentOutOfRangeException("count"); 
    using (StreamReader reader = File.OpenText(path)) { 
     string line; 
     while (count > 0 && (line = reader.ReadLine()) != null) { 
      if (lineIndex > 0) { 
       lineIndex--; // skip 
       continue; 
      } 
      count--; 
      yield return line; 
     } 
    } 
} 
// to read a single line 
public static string ReadLine(string path, int lineIndex) { 
    foreach (string line in ReadLines(path, lineIndex, 1)) { 
     return line; 
    } 
    throw new IndexOutOfRangeException(); 
} 

jeśli trzeba przetestować wartości linii (a nie tylko wskaźnik linii), to jest łatwe wystarczająco dużo do zrobienia; po prostu dostosuj blok iteratora.

1

Jeśli zamierzasz wyszukać wiele różnych linii z pliku (ale nie wszystkie), możesz odnieść korzyść z budowania indeksu podczas podróży. Użyj dowolnej z sugestii, które już tu są, ale gdy będziesz budował tablicę przesunięć bajtowych dla wszystkich linii, które już znalazłeś, abyś mógł zapisać siebie od ponownego skanowania pliku od początku za każdym razem.

Uzupełnienie:
Jest jeszcze jeden sposób można to zrobić szybko, jeśli trzeba tylko okolicznościowy wiersz „losowy”, ale kosztem bardziej skomplikowanego wyszukiwania (Jeśli odpowiedź Jon jest wystarczająco szybki, I” zdecydowanie to trzymam z uwagi na prostotę).

Możesz zrobić "wyszukiwanie binarne", zaczynając od szukania w połowie pliku sekwencji "1", pierwsze znalezione wystąpienie da ci pojęcie, który numer linii znalazłeś; następnie w oparciu o to, gdzie linia, której szukasz, jest względna względem znalezionej liczby, nadal dzielisz rekursywnie.

Dla dodatkowej wydajności można również założyć, że linie są mniej więcej tej samej długości i mają algorytm "zgadywania" przybliżonej pozycji szukanej linii w stosunku do całkowitej liczby linii w pliku, a następnie od tej chwili przeprowadź to wyszukiwanie. Jeśli nie chcesz przyjmować założenia co do długości pliku, możesz zrobić to samo-prime, dzieląc się na pół, a używając numeru linii najpierw znajduje się przybliżenie liczby wierszy w pliku cały.

Zdecydowanie nie jest to łatwe do wdrożenia, ale jeśli masz dużo wolnego dostępu w plikach z dużą liczbą linii, może to przynieść zyski.

0

Jeśli chcesz mieć możliwość przeskoczenia do linii 24 000 za pomocą funkcji, która powoduje, że ReadLine() w tle będzie nieco powolna.

Jeśli numer linii jest wysoki, możesz chcieć zgadnąć, gdzie w pliku może znajdować się wiersz i zacząć z niego czytać. W ten sposób, aby dostać się do linii 24.567, nie musisz najpierw czytać 24.566 wierszy. Możesz przeskoczyć gdzieś pośrodku, dowiedzieć się, w którym wierszu się znajdujesz na podstawie liczby po/t, a następnie liczyć stamtąd.

Jakiś czas temu pracowałem z deweloperem, który musiał zbudować DB, zanim pojawią się RDBMS. Jego rozwiązanie twojego problemu było podobne do tego, o którym pisałem, ale w jego przypadku zachował mapę w osobnym pliku. Mapa może mapować co setną linię do jej lokalizacji w dokumencie. Taką mapę można załadować bardzo szybko, co może wydłużyć czas odczytu. W czasie jego system był bardzo szybki i wydajny dla danych tylko do odczytu, ale niezbyt dobry dla danych do odczytu/zapisu. (za każdym razem, gdy zmieniasz linie, musisz zmienić całą mapę, nie jest to zbyt efektywne)

Powiązane problemy