2009-05-13 8 views
22

Przez formatowanie tekstu miałem na myśli coś bardziej skomplikowanego.Jak analizować plik tekstowy za pomocą C#

Najpierw zacząłem ręcznie dodawać do mojego projektu 5000 linii z pliku tekstowego, do którego zadaję to pytanie.

Plik tekstowy ma 5000 linii o różnej length.For 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 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 

Tekst pomiędzy pierwszym znakiem (1) i drugi znak (1/4/5) nie jest białe znaki, to tab.Nie ma białych znaków w tym pliku tekstowym.

Czego chcę:

Chcę dostać drugą liczbę całkowitą (w trzech liniach napisałem powyżej, drugi całkowitymi są 1,4 i 5), a ciąg w środku każdej linii wskazującej drogę (Zaczyna się od "item \" i kończy się rozszerzeniem pliku ".ddj").

Mój problem:

Kiedy google „Formatowanie tekstu C#” - wszystko, co się ma, jak otworzyć plik tekstowy i jak napisać pliku tekstowego w C# .I nie wiem w jaki sposób wyszukać tekst wewnątrz pliku tekstowego. Również nie mogę wyszukać pierwszej liczby całkowitej, ponieważ w przypadku małej liczby całkowitej, jak w trzech wierszach, które napisałem powyżej, nie będę mógł znaleźć lokalizacji corrent, ponieważ na przykład może istnieć "1" w innej lokalizacji.

Moje pytanie:

To byłby najlepszy Gdybym napisać program, który będzie usuwał wszystko, ale to, czego potrzebuję.

Innym sposobem w moim umyśle jest bezpośrednie przeszukanie w tym pliku, ale jak już wspomniałem powyżej - mógłbym dostać złe położenie drugiej liczby całkowitej, jeśli jest zbyt niska.

Proszę zasugerować coś, nie mogę sformatować tego wszystkiego ręcznie.

+7

"W tym pliku tekstowym nie ma żadnych białych znaków" FYI: znak tabulacji jest odstępem. Masz na myśli „nie ma miejsca w tym pliku tekstowym” –

+0

Oto mój wysiłek: [analizowania linii i ustawienie oddziel aby ciąg csv] [1] [1]: http : //stackoverflow.com/a/27244009/1147352 – DareDevil

Odpowiedz

48

OK, oto co robimy: otwórz plik, przeczytać wiersz po wierszu, i podzielić ją przez zakładkach. Następnie pobieramy drugą liczbę całkowitą i przechodzimy przez resztę, aby znaleźć ścieżkę.

StreamReader reader = File.OpenText("filename.txt"); 
string line; 
while ((line = reader.ReadLine()) != null) { 
    string[] items = line.Split('\t'); 
    int myInteger = int.Parse(items[1]); // Here's your integer. 
    // Now let's find the path. 
    string path = null; 
    foreach (string item in items) { 
     if (item.StartsWith("item\\") && item.EndsWith(".ddj")) { 
      path = item; 
     } 
    } 

    // At this point, `myInteger` and `path` contain the values we want 
    // for the current line. We can then store those values or print them, 
    // or anything else we like. 
} 
+0

Dzięki, przetestuję to, a następnie przekażemy opinię! –

+0

Działa świetnie, dzięki! –

+1

Świetnie. Nie mam kompilatora C# na tym komputerze, więc musiałem go skrzywić. Cieszę się, że to działa po wyjęciu z pudełka. –

5

Można zrobić coś takiego:

using (TextReader rdr = OpenYourFile()) { 
    string line; 
    while ((line = rdr.ReadLine()) != null) { 
     string[] fields = line.Split('\t'); // THIS LINE DOES THE MAGIC 
     int theInt = Convert.ToInt32(fields[1]); 
    } 
} 

powód, dla którego nie znaleźliśmy odpowiedni rezultat, szukając „formatowania” jest to, że operacja wykonujesz nazywa się „parsowania”.

+1

Nie otrzyma "ciągu w środku każdego wiersza wskazującego ścieżkę" (wzięty bezpośrednio z pytania). –

+0

W porządku, bardzo przydatne, ale jak znaleźć ciąg? –

+0

Może być konieczne użycie metody line.Split ("\ t" .ToCharArray()) w zależności od wersji (IIRC) Uważaj jednak. Jeśli chcesz uzyskać dostęp do 15 pozycji na linii, ale linia, nad którą pracujesz zawiera tylko 12 elementów (na przykład), otrzymasz wyjątek. Chronić się przed tego rodzaju rzeczami tak bardzo, jak to możliwe. Również pusta linia wprowadzi cię w chaos (gra słów nie jest przeznaczona), ponieważ polecenie line.split ("\ t") zwróci tablicę z pojedynczym, pustym elementem. – ZombieSheep

0

Spróbuj wyrażeń regularnych. Możesz znaleźć pewien wzór w tekście i zastąpić go czymś, co chcesz. Nie mogę podać teraz dokładnego kodu, ale możesz przetestować swoje wyrażenia, używając tego.

http://www.radsoftware.com.au/regexdesigner/

0

Można otworzyć plik i użyć StreamReader.ReadLine do odczytania pliku wiersz po wierszu. Następnie możesz użyć String.Split, aby podzielić każdą linię na części (użyj ogranicznika \ t), aby wyodrębnić drugą liczbę.

Ponieważ liczba elementów jest różna, należy przeszukać ciąg dla wzoru "element \ * .ddj".

Aby usunąć element, można (na przykład) zachować całą zawartość pliku w pamięci i napisać nowy plik, gdy użytkownik kliknie przycisk "Zapisz".

32

Innym rozwiązaniem, tym razem korzystając z wyrażeń regularnych:

using System.Text.RegularExpressions; 

... 

Regex parts = new Regex(@"^\d+\t(\d+)\t.+?\t(item\\[^\t]+\.ddj)"); 

StreamReader reader = FileInfo.OpenText("filename.txt"); 
string line; 
while ((line = reader.ReadLine()) != null) { 
    Match match = parts.Match(line); 
    if (match.Success) { 
     int number = int.Parse(match.Group(1).Value); 
     string path = match.Group(2).Value; 

     // At this point, `number` and `path` contain the values we want 
     // for the current line. We can then store those values or print them, 
     // or anything else we like. 
    } 
} 

Wyrażenie to jest trochę skomplikowane, więc tutaj to jest w podziale:

^  Start of string 
\d+  "\d" means "digit" - 0-9. The "+" means "one or more." 
     So this means "one or more digits." 
\t  This matches a tab. 
(\d+) This also matches one or more digits. This time, though, we capture it 
     using brackets. This means we can access it using the Group method. 
\t  Another tab. 
.+?  "." means "anything." So "one or more of anything". In addition, it's lazy. 
     This is to stop it grabbing everything in sight - it'll only grab as much 
     as it needs to for the regex to work. 
\t  Another tab. 

(item\\[^\t]+\.ddj) 
    Here's the meat. This matches: "item\<one or more of anything but a tab>.ddj" 
+2

Nie wiem, które z twoich odpowiedzi zaakceptować, oba działają świetnie. Podoba mi się to jeszcze jedno, ponieważ wyjaśniłeś, dlaczego nigdy wcześniej tego nie widziałem! –

+0

Jeśli lubisz wyrażenia regularne, polecam użyć czegoś takiego jak Perl przy następnym przetwarzaniu plików takich jak ten. Jest zaprojektowany wokół nich i można go używać do łatwego formatowania pliku w sposób, jaki lubisz. –

+1

Samir Talwar: Sądzę, że powinieneś zostać nauczycielem regularnych wyrażeń. Sposób, w jaki wszystko wyjaśniłeś, był genialny.Nigdy nie miałem nauczyciela, który był tak szczegółowy! +1 –

1

jak to już wspomniano, gorąco zalecamy użycie wyrażenia regularnego (w System.Text), aby wykonać tego rodzaju pracę.

W połączeniu z solidnym narzędziem, takim jak RegexBuddy, użytkownik zajmuje się obsługą złożonych scenariuszy przetwarzania rekordów tekstowych oraz szybkiego uzyskiwania wyników. Narzędzie sprawia, że ​​jest to naprawdę łatwe.

Nadzieję, że pomaga.

0

Jednym ze sposobów, które naprawdę przydały mi się w takich sytuacjach, jest przejście do starej szkoły i skorzystanie z dostawcy Jet OLEDB wraz z plikiem schema.ini do odczytywania dużych plików rozdzielanych tabulatorami przy użyciu ADO.Net. Oczywiście ta metoda jest użyteczna tylko wtedy, gdy znasz format pliku do zaimportowania.

public void ImportCsvFile(string filename) 
{ 
    FileInfo file = new FileInfo(filename); 

    using (OleDbConnection con = 
      new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=\"" + 
      file.DirectoryName + "\"; 
      Extended Properties='text;HDR=Yes;FMT=TabDelimited';")) 
    { 
     using (OleDbCommand cmd = new OleDbCommand(string.Format 
            ("SELECT * FROM [{0}]", file.Name), con)) 
     { 
      con.Open(); 

      // Using a DataReader to process the data 
      using (OleDbDataReader reader = cmd.ExecuteReader()) 
      { 
       while (reader.Read()) 
       { 
        // Process the current reader entry... 
       } 
      } 

      // Using a DataTable to process the data 
      using (OleDbDataAdapter adp = new OleDbDataAdapter(cmd)) 
      { 
       DataTable tbl = new DataTable("MyTable"); 
       adp.Fill(tbl); 

       foreach (DataRow row in tbl.Rows) 
       { 
        // Process the current row... 
       } 
      } 
     } 
    } 
} 

Po uzyskaniu danych w ładnym formacie, takim jak datatable, odfiltrowanie potrzebnych danych staje się dość banalne.

+1

Konieczne może być zastąpienie JET przez ACE i 4 za pomocą 12 w ciągu połączenia. Upewnij się, że jest skompilowany dla wersji 32-bitowej, a nie 64-bitowej. – TamusJRoyce