Come analizzare un file di testo con C #

Con la formattazione del testo intendevo qualcosa di più complicato.

All’inizio ho iniziato ad aggiungere manualmente le 5000 linee del file di testo per il quale sto facendo questa domanda, nel mio progetto.

Il file di testo ha 5000 linee con lunghezza diversa. Ad esempio:

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 

Il testo tra il primo carattere (1) e il secondo carattere (1/4/5) non è uno spazio bianco, è una scheda. Non ci sono spazi bianchi in quel file di testo.

Quello che voglio:

Voglio ottenere il secondo intero (nelle tre righe che ho postato sopra, i secondi interi sono 1,4 e 5) e la stringa nel mezzo di ogni riga che indica il percorso (inizia con “item \” e termina con estensione del file “.ddj”).

Il mio problema:

Quando google “Text formatting C #” – tutto quello che ottengo è come aprire un file di testo e come scrivere un file di testo in C #. Non so come cercare il testo all’interno di un file di testo. Inoltre non posso cercare per il primo intero, perché nel caso sia un numero intero piccolo come nelle tre righe che ho postato sopra, non sarò in grado di trovare la posizione corretta, perché per esempio “1” potrebbe esistere in una posizione diversa.

La mia domanda:

Sarebbe il migliore Se scrivo un programma che cancellerebbe qualcosa, ma quello di cui ho bisogno.

L’altro modo nella mia mente è di cercare direttamente all’interno di quel file, ma come ho detto sopra, potrei ottenere la posizione sbagliata del secondo intero se è troppo bassa.

Si prega di suggerire qualcosa, non posso formattare tutto questo a mano.

OK, ecco cosa facciamo: apri il file, leggilo riga per riga e dividerlo per tabs. Quindi prendiamo il secondo intero e passiamo attraverso il resto per trovare il percorso.

 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. } 

Un’altra soluzione, questa volta facendo uso di espressioni regolari:

 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. } } 

Quell’espressione è un po ‘complessa, quindi qui è ripartita:

 ^ 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\.ddj" 

Potresti fare qualcosa come:

 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]); } } 

La ragione per cui non hai trovato risultati rilevanti durante la ricerca di ‘formattazione’ è che l’operazione che stai eseguendo è chiamata ‘parsing’.

Come già accennato, raccomando vivamente l’uso dell’espressione regolare (in System.Text) per ottenere questo tipo di lavoro.

In combinazione con uno strumento solido come RegexBuddy , stai cercando di gestire qualsiasi situazione di analisi di record di testo complessa, oltre a ottenere risultati rapidamente. Lo strumento lo rende davvero facile.

Spero che sia d’aiuto.

Un modo che ho trovato davvero utile in situazioni come questa è quello di andare a scuola vecchia e utilizzare il provider OLEDB di Jet, insieme a un file schema.ini per leggere file di grandi dimensioni delimitati da tabulazioni nell’utilizzo di ADO.Net. Ovviamente, questo metodo è davvero utile solo se si conosce il formato del file da importare.

 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... } } } } } 

Una volta che hai i dati in un formato gradevole come un datatable, filtrare i dati che ti servono diventa piuttosto banale.

Prova le espressioni regolari. Puoi trovare un determinato pattern nel tuo testo e sostituirlo con qualcosa che desideri. Non posso darti il ​​codice esatto adesso ma puoi testare le tue espressioni usando questo.

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

È ansible aprire il file e utilizzare StreamReader.ReadLine per leggere il file riga per riga. Quindi puoi usare String.Split per spezzare ogni linea in pezzi (usa un delimitatore \ t) per estrarre il secondo numero.

Poiché il numero di elementi è diverso, è necessario cercare nella stringa il modello “item \ *. Ddj”.

Per eliminare un elemento è ansible (ad esempio) conservare tutti i contenuti del file in memoria e scrivere un nuovo file quando l’utente fa clic su “Salva”.