web-dev-qa-db-fra.com

Comment analyser un fichier texte avec C #

Par formatage de texte, je voulais dire quelque chose de plus compliqué.

Au début, j'ai commencé à ajouter manuellement les 5000 lignes du fichier texte pour lequel je pose cette question dans mon projet.

Le fichier texte comporte 5000 lignes de longueur différente, par exemple:

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

Le texte entre le premier caractère (1) et le deuxième caractère (1/4/5) n'est pas un espace, c'est un onglet. Il n'y a pas d'espaces dans ce fichier texte.

Ce que je veux:

Je veux obtenir le deuxième entier (dans les trois lignes que j'ai publiées ci-dessus, les deuxièmes entiers sont 1,4 et 5) et la chaîne au milieu de chaque ligne indiquant le chemin (il commence par "item \" et se termine par le extension de fichier ".ddj").

Mon problème:

Lorsque je google "Formatage de texte C #" - tout ce que je reçois, c'est comment ouvrir un fichier texte et comment écrire un fichier texte en C #. Je ne sais pas comment rechercher du texte dans un fichier texte. De plus, je ne peux pas rechercher pour le premier entier, parce que dans le cas où c'est un petit entier comme dans les trois lignes que j'ai postées ci-dessus, je ne pourrai pas trouver l'emplacement correct, car par exemple "1" pourrait exister dans un emplacement différent.

Ma question:

Ce serait le meilleur si j'écrivais un programme qui supprimerait n'importe quoi, mais ce dont j'ai besoin.

L'autre façon dans mon esprit est de rechercher directement dans ce fichier, mais comme je l'ai mentionné ci-dessus - je pourrais obtenir le mauvais emplacement du deuxième entier s'il est trop bas.

Veuillez suggérer quelque chose, je ne peux pas formater tout cela à la main.

25
Ivan Prodanov

OK, voici ce que nous faisons: ouvrez le fichier, lisez-le ligne par ligne et divisez-le par onglets. Ensuite, nous saisissons le deuxième entier et parcourons le reste pour trouver le chemin.

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.
}
53
Samir Talwar

Autre solution, utilisant cette fois des expressions régulières:

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

Cette expression est un peu complexe, alors ici elle est décomposée:

^        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"
34
Samir Talwar

Vous pourriez faire quelque chose comme:

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 raison pour laquelle vous n'avez pas trouvé de résultat pertinent lors de la recherche de "formatage" est que l'opération que vous effectuez s'appelle "analyse".

5
erikkallen

Comme cela a déjà été mentionné, je recommanderais fortement d'utiliser l'expression régulière (dans System.Text) pour effectuer ce genre de travail.

En combinaison avec un outil solide comme RegexBuddy , vous envisagez de gérer toutes les situations d'analyse syntaxique d'enregistrements de texte complexes, ainsi que d'obtenir résultats rapidement. L'outil le rend vraiment facile.

J'espère que ça t'as aidé.

1
Vin

Une façon que j'ai trouvée vraiment utile dans des situations comme celle-ci est d'aller à l'ancienne et d'utiliser le fournisseur Jet OLEDB, avec un fichier schema.ini pour lire de gros fichiers délimités par des tabulations en utilisant ADO.Net. De toute évidence, cette méthode n'est vraiment utile que si vous connaissez le format du fichier à importer.

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

Une fois que vous avez les données dans un format sympa comme une table de données, filtrer les données dont vous avez besoin devient assez trivial.

1
Mark Green

Essayez des expressions régulières. Vous pouvez trouver un certain motif dans votre texte et le remplacer par quelque chose que vous voulez. Je ne peux pas vous donner le code exact pour le moment mais vous pouvez tester vos expressions en utilisant ceci.

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

0
Marc Vitalis

Vous pouvez ouvrir le fichier et utiliser StreamReader.ReadLine pour lire le fichier ligne par ligne. Ensuite, vous pouvez utiliser String.Split pour diviser chaque ligne en morceaux (utilisez un délimiteur\t) pour extraire le deuxième nombre.

Comme le nombre d'éléments est différent, vous devrez rechercher dans la chaîne le modèle "élément\*. Ddj".

Pour supprimer un élément, vous pouvez (par exemple) conserver tout le contenu du fichier en mémoire et écrire un nouveau fichier lorsque l'utilisateur clique sur "Enregistrer".

0
Justin Ethier