DLL et CSV

Résolu/Fermé
Theolit
Messages postés
40
Date d'inscription
mercredi 18 mars 2020
Statut
Membre
Dernière intervention
20 avril 2021
- Modifié le 22 févr. 2021 à 13:05
Whismeril
Messages postés
17648
Date d'inscription
mardi 11 mars 2003
Statut
Modérateur
Dernière intervention
10 août 2022
- 20 avril 2021 à 09:52
Bonjour à tous !

Je suis entrain d'écrire une dll en C# pour faciliter l'utilisation de ce type de fichier. (Je ne peux pas utiliser une dll déjà faite)

Pour le moment je récupère les données de mon csv dans une list et j'essaye ensuite de récupérer une ligne de ma list à l'aide d'un match d'un string que je mets en paramètre.
A la base je voulais récupérer un index que je traiterais dans la fonction GetValue() mais si quelqu'un a une meilleure idée ...

Mes problèmes sont:
- Mon programme récupère seulement une cellule sur deux (et je ne vois pas pourquoi)
- Le match ne fonctionne pas, il passe à true pour toutes les cellules (ou presque) malgrès le regex
- Je souhaiterais enlever "nbColumn" et récupérer les colonnes jusqu'à ce que'il n'y est plus de valeurs ( essayé avec reader.EndOfData mais ça semble uniquement fonctionner avec les lignes )

Ci dessous le code en question :

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Microsoft.VisualBasic.FileIO;

namespace CSV
{
    public class CCSV
    {
        public List<List<string>> Tab;

        //Constructeur

        public void Load(string path, string separator)
        {
            Tab = new List<List<string>>(GetCol(path , separator));
        }

        private List<List<string>> GetCol(string path, string separator)   //Recupère le tableau dans une liste
        {

            List<List<string>> RegExValues = new List<List<string>>();
            int nbColumn = 27; //Faire en sorte que nbColumn s'arrete quand il n'y a plus de valeur dans les colonnes!!
            int i;
            string indexLine = "0";
            int count = 0;
            try
            {
               
                TextFieldParser reader = new TextFieldParser(path);
                reader.TextFieldType = FieldType.Delimited;
                reader.SetDelimiters(separator);
               

                while (!reader.EndOfData)
                {
                   
                        //Process row
                        List<string> AllDataLine = new List<string>(reader.ReadFields());
                        List<string> Line = new List<string>();
                        for ( i = 0; i < nbColumn; i++)
                        {
                            Line.Add(AllDataLine[i]);
                            i++;
                        }


                    if ( AllDataLine[0] != "LigneIndesirable") //Evite l'ajout de la ligne LigneIndésirable + ajoute une cellule "index"
                    {
                        indexLine = count.ToString();
                        Line.Add(indexLine);
                        count++;
                        RegExValues.Add(Line);
                    }

                    else
                    {
                        reader.ReadFields();
                    }
                }
                reader.Close();

            }
            catch (Exception ex)
            {
                //Gérer les erreur avec loggers
            }
            return RegExValues;


        }

        public string GetValue(string MLFB, string ValueName)
        {
            string result = "";

            return result;
        }


        public int GetIndex(string value)
        {
            int index = 0;
            int result = 0;

            foreach (List<string> line in Tab)
            {
                foreach (string col in line)
                {
                    Match match = Regex.Match(value, col); 
                    if (match.Success)
                    {
                        result = index;
                    }
                }
                index++;
            }

            return index;
        }

    }
}





Merci d'avance :)

24 réponses

Whismeril
Messages postés
17648
Date d'inscription
mardi 11 mars 2003
Statut
Modérateur
Dernière intervention
10 août 2022
850
22 févr. 2021 à 18:58
D'abord, parser un csv ça prend beaucoup moins de lignes que tu en as écrit.
Même si le nombre de lignes n'est pas un critère de qualité de code, écrire des trucs en trop c'est perdre du temps.

Soit ce csv

Nom;Prénom;Sexe;Date de naissance
Sors;Jean;M;01/01/2001
Zétofrais;Mélanie;F;02/02/2002


Et voilà pour le parser
            List<List<string>> lesLignes = (from l in File.ReadAllLines("test.csv")
                                             select l.Split(';').ToList()
                                             ).ToList();


Résultat


Ok, là ça n'exclut pas la ligne d'entêtes, pas de problèmes

Option 1, on part du principe qu'il y a toujours une ligne d'entêtes => on zappe la première ligne
            List<List<string>> lesLignes = (from l in File.ReadAllLines("test.csv").Skip(1)
                                             select l.Split(';').ToList()
                                             ).ToList();


Option 2, on est pas sûr que cette ligne soit systématiquement présente, mais quand elle est là elle correspond à une condition, pour l'exemple, je dis qu'elle commence par Nom;
            List<List<string>> lesLignes = (from l in File.ReadAllLines("test.csv")
                                            where !l.StartsWith("Nom;")
                                             select l.Split(';').ToList()
                                             ).ToList();


Option 3, tu ne sais pas à quoi correspond la ligne d'entête, mais dans les lignes valables, la dernière colonne contient une date
            List<List<string>> lesLignes = (from l in File.ReadAllLines("test.csv")
                                            let split = l.Split(';').ToList()
                                            where DateTime.TryParse(split.Last(), out DateTime d)
                                             select split
                                             ).ToList();


Et j'en passe....
Brefs tu peux imaginer tout ce qu'une simple requête Linq peut t'apporter.

Une fois que tu as ta liste de listes de strings. Pour rechercher une ligne dont un colonne correspond à un critère, ben Linq

            List<string> Annee2001 = (from l in lesLignes
                                      where l.Last().EndsWith("/2001")
                                      select l
                                    ).FirstOrDefault();


j'aurais pu mettre une regex aussi.
Ce qui donne



FirstOrDefaut est ici par fainéantise, j'ai forcé la requête à retourner la première occurence (s'il y en a plusieurs) ou une liste vide s'il n'y en a pas.
On peut facilement gérer plusieurs occurrences, par exemple avec une nouvelle liste de listes de string.

Mais en fait, tout ça ne sert à rien.
En l'état ton code considère qu'un csv c'est un fichier contenant des lignes qui contiennent des colonnes de texte. Et que C# est un langage qui ne traite que du texte.

C'est faux pour la très grande majorité des csv. A de rares exceptions près, un csv contient une donnée par ligne et cette donnée contient des champs.
Et c'est faux pour C#, car c'est un langage pensé pour manipuler des collections d'objets.

Soit donc l'object Contact que voilà
    class Contact
    {
        //Les propriétés qui correspondent aux champs du csv
        public string Nom { get; set; } 

        public string Prenom { get; set; }

        public DateTime DateNaissance { get; set; }

        public Sexe Sexe { get; set; }


        //d'autres propriétés et des méthodes utiles au déroulement du programme
        public string NomComplet { get { return string.Format("{0} {1}", Prenom, Nom); } }

        public int Age { get { return DateTime.Now.Year - DateNaissance.Year; } }

        public override string ToString()
        {
            return string.Format("{0}, {3} de  {1} an{2}", NomComplet, Age, Age > 1 ? "s" : "", Sexe == Sexe.Feminin ? "femme" : Sexe == Sexe.Masculin ? "homme" : "personne" ) ;
        }
    }

    public enum Sexe
    {
        Inconnu,
        Feminin,
        Masculin
    }
}


Comme tu le voies, parser un csv directement dans une collection dont l'objet est adapté à la donnée stockée ne prends que quelques lignes.
Faire des recherches personnalisée et performantes aussi.
Je traite tous les jours des fichiers textes de toutes sortes et ça va tellement vite d'adapter que je n'ai jamais ressenti le besoin d'une dll dédiée.

En plus, créer un code générique dans une dll pour parser n'importe quel csv en n'importe quel objet près définit, c'est probablement possible, mais pas super simple.
Il va falloir écrire des méthodes génériques dans lesquels on transmettrait l'objet à parser et la façon dont on le parse.


On peut le parser comme ça
            List<Contact> lesContacts = (from l in File.ReadAllLines("test.csv").Skip(1)
                                            let split = l.Split(';')
                                            select new Contact
                                            {
                                                 Nom = split[0],
                                                 Prenom = split[1],
                                                 Sexe = split[2] == "F" ? Sexe.Feminin : split[2] == "M" ? Sexe.Masculin : Sexe.Inconnu,
                                                 DateNaissance = DateTime.Parse(split[3])
                                            }
                                             ).ToList();

Ce qui donne


Et on peut faire des recherches sur des "vraies" données, par exmple

            Contact Annee2001 = (from c in lesContacts
                                      where c.DateNaissance.Year == 2001
                                      select c
                                    ).FirstOrDefault();

ou encore
            List<Contact> Majeurs = (from c in lesContacts
                                     where c.Age > 17
                                     select c
                                    ).ToList();


Qui donnent



1
Theolit
Messages postés
40
Date d'inscription
mercredi 18 mars 2020
Statut
Membre
Dernière intervention
20 avril 2021

23 févr. 2021 à 15:02
Merci énormément pour ton retour très complet.
Effectivement beaucoup plus simple avec linq :D
Cependant je suis obligé de faire une dll , donc me gratter un peu la tête pour l'adapter à un maximum de fichier.
Sachant que un fois les données récupéré, je dois potentiellement retrouver une ligne spécifique à l'aide de la colonne "Donnée_RegEx" et également renvoyer une valeur en passant en paramètre laVariable + leNomDeLaColonne (exemple: GetValue("RegEx", "Temperature", "Lower_Tol");
Ma fonction va alors retrouver la variable "Temperature" , descendre jusqu'à Nominal_Value_1, récupérer le 1, chercher la colonne "Lower_Tol_1" et récupérer la valeur qui match entre la colonne "Lower_Tol_1" et la ligne de la RegEx.. Voilà plus ou moins mon but final



Je vais essayer d'écrire quelque chose dans la semaine, je publierais le résultat ici !
Merci encore,
0
Whismeril
Messages postés
17648
Date d'inscription
mardi 11 mars 2003
Statut
Modérateur
Dernière intervention
10 août 2022
850
23 févr. 2021 à 18:48
J’ai rien compris....

Cette capture, c’est le csv affiché dans Excel ?

Tu parles d’un 1 dans la colonne Nominal_Value_1 mais on ne voit que -5 et -0.5.

Enfin ça doit matcher entre la colonne regex et la colonne Lower_Tol_1 mais les 2 exemples que tu montres dans la colonne regex ne peut pas matcher avec un nombre, et on ne voit que des nombres dans la colonne Lower_Tol_.

Bref, le peu que je crois comprendre ne colle pas avec l’exemple que tu montres...
1
Theolit
Messages postés
40
Date d'inscription
mercredi 18 mars 2020
Statut
Membre
Dernière intervention
20 avril 2021

24 févr. 2021 à 08:19
Oui c'est le CSV affiché dans un Excel (Plus facile pour expliquer)

Le 1 qui est récupéré est celui de Nominal_Value_1
Ce qui permettra de retrouver les colonnes Lower et Upper même si on change l'ordre

Ce n'est pas RegEx et une valeur qui doivent matcher mais :
- Je recherche une ligne spécifique avec le RegEx
- Dans cette ligne je suis à la recherche de " Lower_Tol_1" Mais dans le code je peux uniquement mettre le nom de la variable, ici "Temperature"
- Je dois pouvoir gérer l'inversion des colonnes donc j'utilise l'indice derrière chaque colonne (_1 ou _2 pour l'exemple)

Je ne sais pas si c'est plus clair ...
0
Whismeril
Messages postés
17648
Date d'inscription
mardi 11 mars 2003
Statut
Modérateur
Dernière intervention
10 août 2022
850
1 mars 2021 à 20:05
Bon j'aurais eu d'autres questions, mais j'ai émis des hypothèses (elles sont en commentaire dans le code).
Si elle ne sont pas exactes, faudra me le dire

Soit le CSV

Name:;test;;;;Modification;;var1;Température;;var2;Pression;var3;Hygrométrie;;;
Nr:;123;;;;Comments;;Nom Var;Temp;;nameVar2;;;;;;
Cout ;1234;;;;Control;;;;;;;;;;;
Modification date:;23/02/2021;;;;;;;;;;;;;;;
Resp of modif.:;moi;;;;;;Unit;deg C;;;;;;;;
File Name;test;;;;;;;;;;;;;;;
File Place;c:\Temp;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;
Name;Type;Yeux;Taille;Donnée_RegEx;donnée_test;blabla;bla;Nominal_Value_1;Lower_Tol_1;Upper_Tol_1;Nominal_Value_2;Lower_Tol_2;Nominal_Value_3;Lower_Tol_3;Upper_Tol_3;Autre_3
Pauline;Feminin;bleu;170;BLA-.P[12]...B.170;.;.;.;-5;-1;1;0;-60;1;2;3;4
Maxime;Masculin;vert;190;ALB-.M[12]...V.190;.;.;.;-0,5;-0,2;0,2;0;-19;1;2;3;4

Ce qui vu d'Excel donne


Voici d'abord 3 classes qui permettent de structurer les données
    /// <summary>
    /// Correspond à un relevé pour un mesurande
    /// </summary>
    class Mesurande
    {
        public Mesurande(string LeNom, Dictionary<string, double> LesDatas)
        {
            Nom = LeNom;
            Datas = LesDatas;
        }

        public string Nom { get; private set; }

        public Dictionary<string, double> Datas { get; private set; }

        public override string ToString()
        {
            return string.Format("{0}, {1} valeurs", Nom, Datas.Count);
        }

    }

    /// <summary>
    /// Permet de construire un mesurande à partir d'un fichier
    /// </summary>
    class PrototypeMesurande
    {
        public PrototypeMesurande(string LeNom, int LIndex, string[] LesEntetes)
        {
            Nom = LeNom;
            Index = LIndex;
            Entetes = LesEntetes.ToList();
        }

        public string Nom { get; private set; }

        public int Index { get; private set; }

        public List<string> Entetes { get; set; }
    }

    /// <summary>
    /// Correspond à une ligne de mesure
    /// </summary>
    class DonneeTheolit
    {
        public DonneeTheolit(string LeNom, string LaDonneeRegex, List<Mesurande> LesMesurandes)
        {
            Nom = LeNom;
            DonneeRegex = LaDonneeRegex;
            Mesurandes = LesMesurandes;
        }

        public string Nom { get; private set; }

        public string DonneeRegex { get; private set; }

        public List<Mesurande> Mesurandes { get; private set; }

        public override string ToString()
        {
            return string.Format("{0}, {1} mesurandes", Nom, Mesurandes.Count);
        }
    }


Puis la classe qui lit le fichier et met ça en musique
    /// <summary>
    /// Classe qui gère le tout
    /// </summary>
    class ImportDonneTheolit
    {
        private string filename;

        /// <summary>
        /// Construit une instance de DonneeTheolit à partir d'un csv
        /// </summary>
        /// <param name="Filename"></param>
        public ImportDonneTheolit(string Filename)
        {
            filename = Filename;
            if (!File.Exists(Filename))
                throw new Exception("Fichier introuvable");

            if (Path.GetExtension(Filename).ToLower() != ".csv")
                throw new Exception("Mauvaise extension");

            //Lecture du fichier
            string[] lesLignes = File.ReadAllLines(Filename, Encoding.Default);

            //Hypothèse 1, les métadatas sont toujours dans les 10 premières lignes
            List<string[]> metadatas = (from l in lesLignes.Take(10)
                                        select l.Split(';')
                                        ).ToList();

            //Hypothèse 2, le nom d'un mesurande est toujours précédé de varXXX
            // => on peut extraire l'index de début de chaque mesurande
            List<int> indexMesurandes = metadatas[0].Select((texte, ind) => new { texte, ind }).Where(x => Regex.IsMatch(x.texte, @"^var\d+$")).Select(x => x.ind + 1).ToList();

            //Hypothèse 3, y'a pas de colonne vide entre 2 mesurandes
            // => on peut extraire les prototypes, sans s'embêter avec les _XX
            metadatas[9] = (from x in metadatas[9]
                            let m = Regex.Match(x, @"_\d+$")
                            select m.Success ? x.Replace(m.Value, "") : x
                            ).ToArray();

            int i, index1;
            List<PrototypeMesurande> prototypes = new List<PrototypeMesurande>();

            for (i = 0; i < indexMesurandes.Count - 1; i++)
            {
                index1 = indexMesurandes[i];
                int index2 = indexMesurandes[i + 1];
                prototypes.Add(new PrototypeMesurande(metadatas[0][index1], index1, metadatas[9].Skip(index1).Take(index2 - index1).ToArray()));
            }
            //le dernier mesurande s'arrête à la fin de la ligne
            index1 = indexMesurandes[i];
            prototypes.Add(new PrototypeMesurande(metadatas[0][index1], index1, metadatas[9].Skip(index1).ToArray()));

            //import des données
            Datas = (from l in lesLignes.Skip(10)
                     let split = l.Split(';')
                     select new DonneeTheolit(split[0], split[4], ToMesurandes(split, prototypes))
                     ).ToList();
        }

        private List<Mesurande> ToMesurandes(string[] Split, List<PrototypeMesurande> Prototypes)
        {
            List<Mesurande> m = new List<Mesurande>();
            foreach (PrototypeMesurande p in Prototypes)
            {
                m.Add(new Mesurande(p.Nom,
                                        (from e in p.Entetes
                                        let indexE = p.Entetes.IndexOf(e)
                                        let indexS = p.Index + indexE
                                        select new { Key = p.Entetes[indexE], Value = Convert.ToDouble(Split[indexS]) }
                                        ).ToDictionary(x => x.Key, x => x.Value)
                        ));
            }


            return m;

        }

        public List<DonneeTheolit> Datas { get; private set; }

        /// <summary>
        /// Retourn eun mesurande selon la colonne DonneeRegex et son nom
        /// </summary>
        /// <param name="Nom"></param>
        /// <param name="DonneeRegex"></param>
        /// <returns></returns>
        public Mesurande TrouveMesurande(string Nom, string DonneeRegex)
        {
            return Datas.FirstOrDefault(d => d.DonneeRegex == DonneeRegex)?.Mesurandes.FirstOrDefault(m => m.Nom == Nom);
        }

        public override string ToString()
        {
            return string.Format("{0}, {1} lignes de mesures", filename, Datas.Count);
        }
    }


Enfin le code qui utilise cette classe
            ImportDonneTheolit test = new ImportDonneTheolit("TestTheolit.csv");

            Mesurande m = test.TrouveMesurande("Pression", "ALB-.M[12]...V.190");


Ce qui donne





1
Theolit
Messages postés
40
Date d'inscription
mercredi 18 mars 2020
Statut
Membre
Dernière intervention
20 avril 2021

2 mars 2021 à 15:15
Merci pour ton retour détaillé, c'est exactement ça!

Dernière question (normalement ^^ ):

Si je veux juste trouver une valeur avec par exemple :

Mesurande m = test.TrouveMesurande("Pression","Lower_Tol", "ALB-.M[12]...V.190");


Comment je dois procéder ?
0
Whismeril
Messages postés
17648
Date d'inscription
mardi 11 mars 2003
Statut
Modérateur
Dernière intervention
10 août 2022
850
Modifié le 2 mars 2021 à 18:16
Alors ton problème c'est que tu n'as pas compris ce qu'est un objet.
Et C# c'est un langage "tout" objet.

Ça
Mesurande m = test.TrouveMesurande("Pression","Lower_Tol", "ALB-.M[12]...V.190");


c'est à la fois, inutile et "impossible".
Impossible puisque cet objet Mesurande encapsule toutes les valeurs de pression qui correspondent à ALB-.M[12]...V.190. On ne peut donc pas mettre cette valeurs (qui est un double) dans un Mesurande.
Et donc c'est inutile car m contient déjà cette valeur, il suffit de lui "prendre".

Le meilleur conseil que je puisse te donner c'est de trouver un cours ou un tuto sur l'objet avant d'aller plus loin dans te prochains.

J'en ai écrit un qui s'adresse aux autodidactes, c'est pas académique, mais c'est basé sur mes propres ratés.
https://codes-sources.commentcamarche.net/faq/11239-la-programmation-objet-appliquee-a-net-par-l-exemple-partie-1-sur-3

Mais y'en a pleins d'autres.

Quand j'étais petit, la mer Morte n'était que malade.
George Burns
1
Theolit
Messages postés
40
Date d'inscription
mercredi 18 mars 2020
Statut
Membre
Dernière intervention
20 avril 2021

3 mars 2021 à 16:14
Je vais me renseigner , merci pour tes conseils et ton aide.
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
Whismeril
Messages postés
17648
Date d'inscription
mardi 11 mars 2003
Statut
Modérateur
Dernière intervention
10 août 2022
850
1 avril 2021 à 21:01
J'avais bien compris que tu voulais t'en servir.
Et à raison, manifestement.
Mais tes explications initiales étaient trop floues et je n'ai pas vu l'intérêt de la chose.

Bon j'ai pu générer mes prototypes avec ces numéros.
Mais il y'a 2 nouveaux problèmes avec ce fichier.

D'abord la dernière ligne. Une ligne commMLFB_RegExe ça est-elle toujours présente?
Ensuite dans le premier fichier la colonne que tu avais appelées "Données-Regex" était la colonne E. Dans le second, il y a 3 colonnes dont les valeurs y ressemble, O, P et S. Celle qui y ressemble le plus est la O est s'appelle MLFB_RegEx. Est ce bien cette colonne et, s'appelle t elle toujours ainsi?

Question subsidiaire, y a t il toujours une colonne "Nber_of_Variable" (même si j'ai fait sans, ça peut permettre de vérifier)
1
Theolit
Messages postés
40
Date d'inscription
mercredi 18 mars 2020
Statut
Membre
Dernière intervention
20 avril 2021

4 avril 2021 à 21:37
Désolé pour la réponse tardive ( fête oblige, d'ailleurs bonnes fêtes de Pâques s'il y a lieu),
la colonne MLFB_RegExe sera toujours présente mais pas forcement au même endroit (peut être décalé d'une ou deux colonne)

La colonne "Données-Regex" est bien équivalente à "MLFB_RegEx". En effet, il s'agit du nouveau nom de cette colonne, ce sera donc toujours "MLFB_RegEx".

La colonne "Nber_of_Variable" n'est malheureusement pas présente dans la totalité des fichiers .
0
Whismeril
Messages postés
17648
Date d'inscription
mardi 11 mars 2003
Statut
Modérateur
Dernière intervention
10 août 2022
850 > Theolit
Messages postés
40
Date d'inscription
mercredi 18 mars 2020
Statut
Membre
Dernière intervention
20 avril 2021

4 avril 2021 à 22:01
Joyeuses Pâques à toi aussi.

Et la dernière ligne. Est elle toujours présente?
0
Theolit
Messages postés
40
Date d'inscription
mercredi 18 mars 2020
Statut
Membre
Dernière intervention
20 avril 2021
> Whismeril
Messages postés
17648
Date d'inscription
mardi 11 mars 2003
Statut
Modérateur
Dernière intervention
10 août 2022

5 avril 2021 à 10:51
Merci !

Si tu parles de la ligne où il y a la somme du nombre de ligne, oui, elle est encore présente.
0
Whismeril
Messages postés
17648
Date d'inscription
mardi 11 mars 2003
Statut
Modérateur
Dernière intervention
10 août 2022
850
6 avril 2021 à 17:37
Quand je teste avec ton 2eme fichier, les valeurs
 Mesurande m = test.TrouveMesurande("Current", "BLA-.UH[48]0...E..22");


Ca marche

1
Theolit
Messages postés
40
Date d'inscription
mercredi 18 mars 2020
Statut
Membre
Dernière intervention
20 avril 2021

19 avril 2021 à 14:42
Bonjour,

Je n'ai pas eu le temps de regarder ta réponse avant, désolé.
Mais je vois pourquoi ça ne fonctionne pas chez moi.
Je pense mettre mal exprimé (encore une fois) .. Pour la ligne ci dessous :

Mesurande m = test.TrouveMesurande("Current", "BLA-.UH[48]0...E..22");


Ici, tu mets déjà la donnée en regex alors que je pensais mettre une donnée complète du genre :

Mesurande m = test.TrouveMesurande("Current", "BLA-4UH80321EAB22");


Et donc ajouter un "match" ici: Pour faire matcher "BLA-.UH[48]0...E..22" du csv et "BLA-4UH80321EAB22" en entrée

  public Mesurande TrouveMesurande(string Nom, string DonneeRegex)
        {
            return Datas.FirstOrDefault(d => d.DonneeRegex == DonneeRegex)?.Mesurandes.FirstOrDefault(m => m.Nom == Nom);
        }



Merci :)
0
Whismeril
Messages postés
17648
Date d'inscription
mardi 11 mars 2003
Statut
Modérateur
Dernière intervention
10 août 2022
850 > Theolit
Messages postés
40
Date d'inscription
mercredi 18 mars 2020
Statut
Membre
Dernière intervention
20 avril 2021

19 avril 2021 à 16:09
Ha oui, il tu dois ajouter une regex là en effet, un truc du genre

d => Regex.IsMatch(DonneeRegex, d.DonneeRegex)
0
Theolit
Messages postés
40
Date d'inscription
mercredi 18 mars 2020
Statut
Membre
Dernière intervention
20 avril 2021
> Whismeril
Messages postés
17648
Date d'inscription
mardi 11 mars 2003
Statut
Modérateur
Dernière intervention
10 août 2022

20 avril 2021 à 09:22
Ca fonctionne ! Merci encore pour toutes tes explications.
0
Whismeril
Messages postés
17648
Date d'inscription
mardi 11 mars 2003
Statut
Modérateur
Dernière intervention
10 août 2022
850 > Theolit
Messages postés
40
Date d'inscription
mercredi 18 mars 2020
Statut
Membre
Dernière intervention
20 avril 2021

20 avril 2021 à 09:52
De rien
0
Whismeril
Messages postés
17648
Date d'inscription
mardi 11 mars 2003
Statut
Modérateur
Dernière intervention
10 août 2022
850
22 févr. 2021 à 13:30
Bonjour

Je n’ai pas le temps de suite pour regarder ton code en détail.
Mais tu ne connais pas linq?

Plus besoin de reader, de boucles, etc...
0
Theolit
Messages postés
40
Date d'inscription
mercredi 18 mars 2020
Statut
Membre
Dernière intervention
20 avril 2021

23 févr. 2021 à 14:02
Non , malheureusement je ne connais pas linq..
0
Whismeril
Messages postés
17648
Date d'inscription
mardi 11 mars 2003
Statut
Modérateur
Dernière intervention
10 août 2022
850
24 févr. 2021 à 19:02
Maintenant je comprends le 1 mais pas Température.

Peux tu poster sur cjoint ou un service équivalent un fichier représentatif (sans données "confidentielles" bien sûr) et expliquer dans un message ce que tu veux mettre en données d'entrées, et ce que tu veux avoir en sortie.
0
Theolit
Messages postés
40
Date d'inscription
mercredi 18 mars 2020
Statut
Membre
Dernière intervention
20 avril 2021

Modifié le 25 févr. 2021 à 13:09
Le doc: https://www.cjoint.com/c/KBzlZIar8Oy

Je souhaiterais entrer en paramètre :
- un string qui devra matcher avec une expression de la colonne RegEx (Exemple: BLA-4P1256BA170 )
- le nom d'une variable que je veux récupérer (Exemple : Temperature)
- le nom de la colonne souhaitée ( Exemple : Upper_Tol)

Pour rappel:
le programme va chercher "Température", trouver "Nominal_Value_1", récupérer le "_1" , et l'ajouter à la colonne qu'on souhaite récupérer (ici : Upper_Tol)

Exemple d'utilisation :

test.GetValue("BLA-4P1256BA170", "Température", "Upper_Tol"); //J'enlèverais les accents ^^


Ce qui devrait me renvoyer : "1"

Pour info , j'ai déjà codé la partie de récupération et ajout du chiffre (Exemple "_1")

   private string GetIndexName(string name) //Récupère l'indice (Exemple "_1" pour "Nominal_Value_1")
        {
            string result;
            int indice = name.LastIndexOf('_');
            result = name.Substring(indice);

            return result;
        }

        private string AddIndexName(string nameGet, string nameAdd) //Ajoute l'indice derriere la colonne que l'on veut récupérer (Exemple "Lower_Value" + "_1")
        {
            string result;
            result = nameAdd + GetIndexName(nameGet);
            return result; 
        }



Merci :)
0
Whismeril
Messages postés
17648
Date d'inscription
mardi 11 mars 2003
Statut
Modérateur
Dernière intervention
10 août 2022
850
25 févr. 2021 à 17:52
Si ta capture m'avait permis de comprendre, je ne t'aurais pas demandé un fichier représentatif
Envoyer ta capture ne sert à rien.

Et je ne comprend toujours pas ce que vient faire Température là dedans, j'avais espéré comprendre avec un fichier représentatif

0
Whismeril
Messages postés
17648
Date d'inscription
mardi 11 mars 2003
Statut
Modérateur
Dernière intervention
10 août 2022
850
25 févr. 2021 à 18:52
Tu peux m’envoyer le lien par MP si tu préfères
0
Theolit
Messages postés
40
Date d'inscription
mercredi 18 mars 2020
Statut
Membre
Dernière intervention
20 avril 2021

1 mars 2021 à 08:17
Je ne comprend pas, le lien Cjoint renvoi vers mon fichier représentatif (j'ai juste changé le nom des colonnes)

Température est le nom de la "variable" qui contient 3 données:
- Nominal_Value_X
-Lower_Tol_X
-Upper_Tol_X

Avec X l'indice (Exemple : "_1" )

Malheureusement je ne peux pas changer la structure du CSV..
0
Whismeril
Messages postés
17648
Date d'inscription
mardi 11 mars 2003
Statut
Modérateur
Dernière intervention
10 août 2022
850
1 mars 2021 à 12:13
Ok, donc plus loin on pourrait imaginer une pression avec elle aussi plusieurs colonnes de données.

On va arrêter de parler de variable qui a plusieurs sens, dont un en développement, mais de mesurande (phénomène physique à mesurer) qui n’a qu’un sens qui correspond à ton tableau.

Les colonnes de données par mesurande sont elles toujours au nombre de 3?

0
Theolit
Messages postés
40
Date d'inscription
mercredi 18 mars 2020
Statut
Membre
Dernière intervention
20 avril 2021

1 mars 2021 à 14:46
Exactement, il peut y avoir plusieurs mesurande.

Malheureusement non, un mesurande a un nombre de colonne variable.
D'où le fait que je récupère "l'indice".
0
Whismeril
Messages postés
17648
Date d'inscription
mardi 11 mars 2003
Statut
Modérateur
Dernière intervention
10 août 2022
850
5 mars 2021 à 12:48
Bonjour

Suite à ton MP, je ne conçois le MP que s’il y a un besoin impérieux de discrétion.
Je te réponds donc ici.

Il faut que la classe soit public, pas seulement le constructeur.
Il faut probablement aussi que la classe Mesurande soit public.

Si c’est le cas, il faudra poster le code et peut-être aussi des captures d’écran.
0
Theolit
Messages postés
40
Date d'inscription
mercredi 18 mars 2020
Statut
Membre
Dernière intervention
20 avril 2021

22 mars 2021 à 08:52
Bonjour, et désolé pour ma réponse tardive,

Le fait de mettre "public" devant la classe ImportDonneTheolit me donne ça :



Merci d'avance,
0
Whismeril
Messages postés
17648
Date d'inscription
mardi 11 mars 2003
Statut
Modérateur
Dernière intervention
10 août 2022
850
22 mars 2021 à 09:55
Il faut que cette propriété soit public aussi
0
Theolit
Messages postés
40
Date d'inscription
mercredi 18 mars 2020
Statut
Membre
Dernière intervention
20 avril 2021

22 mars 2021 à 10:39
D'accord, mais où dois-je ajouter ce public ?
Sachant que "Datas" est présent là:

 public ImportDonneTheolit(string Filename)
        {
 Datas = (from l in lesLignes.Skip(10)
                     let split = l.Split(';')
                     select new DonneeTheolit(split[0], split[4], ToMesurandes(split, prototypes))
                     ).ToList();
        }


et là :

 public List<DonneeTheolit> Datas { get; private set; }
0
Whismeril
Messages postés
17648
Date d'inscription
mardi 11 mars 2003
Statut
Modérateur
Dernière intervention
10 août 2022
850
22 mars 2021 à 11:03
A la ligne où tu as l’erreur
0
Theolit
Messages postés
40
Date d'inscription
mercredi 18 mars 2020
Statut
Membre
Dernière intervention
20 avril 2021

22 mars 2021 à 11:22
Justement c'est ça mon problème, il y a déjà "public" devant

 public List<DonneeTheolit> Datas { get; private set; }
0
Whismeril
Messages postés
17648
Date d'inscription
mardi 11 mars 2003
Statut
Modérateur
Dernière intervention
10 août 2022
850
22 mars 2021 à 11:40
Ha, je n’ai pas bien vu l’image peut-être.

Et la classe DonneeTheolit?
0
Theolit
Messages postés
40
Date d'inscription
mercredi 18 mars 2020
Statut
Membre
Dernière intervention
20 avril 2021

22 mars 2021 à 12:29
C'était bien ça, merci!

Maintenant je rencontre d'autres problèmes,
Comme :


ou alors: (En utilisant que l'hypothèse 1, pas de données dans la list "m")



J'ai essayé en enlevant les différentes hypothèses mais j'ai soit l'erreur ci-dessus, soit le programme ne me ressort rien (pas de mesurande). Sachant que ce serait l'hypothèse 1 qui m'intéresserait.



Rappel du code:



   /// <summary>
    /// Correspond à un relevé pour un mesurande
    /// </summary>
   public class Mesurande
    {
        public Mesurande(string LeNom, Dictionary<string, double> LesDatas)
        {
            Nom = LeNom;
            Datas = LesDatas;
        }

        public string Nom { get; private set; }

        public Dictionary<string, double> Datas { get; private set; }

        public override string ToString()
        {
            return string.Format("{0}, {1} valeurs", Nom, Datas.Count);
        }

    }

    /// <summary>
    /// Permet de construire un mesurande à partir d'un fichier
    /// </summary>
    class PrototypeMesurande
    {
        public PrototypeMesurande(string LeNom, int LIndex, string[] LesEntetes)
        {
            Nom = LeNom;
            Index = LIndex;
            Entetes = LesEntetes.ToList();
        }

        public string Nom { get; private set; }

        public int Index { get; private set; }

        public List<string> Entetes { get; set; }
    }

    /// <summary>
    /// Correspond à une ligne de mesure
    /// </summary>
    public class DonneeTheolit
    {
        public DonneeTheolit(string LeNom, string LaDonneeRegex, List<Mesurande> LesMesurandes)
        {
            Nom = LeNom;
            DonneeRegex = LaDonneeRegex;
            Mesurandes = LesMesurandes;
        }

        public string Nom { get; private set; }

        public string DonneeRegex { get; private set; }

        public List<Mesurande> Mesurandes { get; private set; }

        public override string ToString()
        {
            return string.Format("{0}, {1} mesurandes", Nom, Mesurandes.Count);
        }
    }

    /// <summary>
    /// Classe qui gère le tout
    /// </summary>
   public class ImportDonneTheolit
    {
        private string filename;

        /// <summary>
        /// Construit une instance de DonneeTheolit à partir d'un csv
        /// </summary>
        /// <param name="Filename"></param>
        public ImportDonneTheolit(string Filename)
        {
            filename = Filename;
            if (!File.Exists(Filename))
                throw new Exception("Fichier introuvable");

            if (Path.GetExtension(Filename).ToLower() != ".csv")
                throw new Exception("Mauvaise extension");

            //Lecture du fichier
            string[] lesLignes = File.ReadAllLines(Filename, Encoding.Default);

            //Hypothèse 1, les métadatas sont toujours dans les 10 premières lignes 
            List<string[]> metadatas = (from l in lesLignes.Take(10)
                                        select l.Split(';')
                                        ).ToList();

            //Hypothèse 2, le nom d'un mesurande est toujours précédé de varXXX
            // => on peut extraire l'index de début de chaque mesurande
            List<int> indexMesurandes = metadatas[0].Select((texte, ind) => new { texte, ind }).Where(x => Regex.IsMatch(x.texte, @"^var\d+$")).Select(x => x.ind + 1).ToList();

            // Hypothèse 3, y'a pas de colonne vide entre 2 mesurandes
            // => on peut extraire les prototypes, sans s'embêter avec les _XX
            metadatas[9] = (from x in metadatas[9]
                            let m = Regex.Match(x, @"_\d+$")
                            select m.Success ? x.Replace(m.Value, "") : x
                            ).ToArray();

            int i, index1;
            List<PrototypeMesurande> prototypes = new List<PrototypeMesurande>();

            for (i = 0; i < indexMesurandes.Count - 1; i++)
            {
                index1 = indexMesurandes[i];
                int index2 = indexMesurandes[i + 1];
                prototypes.Add(new PrototypeMesurande(metadatas[0][index1], index1, metadatas[9].Skip(index1).Take(index2 - index1).ToArray()));
            }
            //le dernier mesurande s'arrête à la fin de la ligne
            index1 = indexMesurandes[i];
            prototypes.Add(new PrototypeMesurande(metadatas[0][index1], index1, metadatas[9].Skip(index1).ToArray()));

            //import des données
            Datas = (from l in lesLignes.Skip(10)
                     let split = l.Split(';')
                     select new DonneeTheolit(split[0], split[4], ToMesurandes(split, prototypes))
                     ).ToList();
        }

        private List<Mesurande> ToMesurandes(string[] Split, List<PrototypeMesurande> Prototypes)
        {
            List<Mesurande> m = new List<Mesurande>();
            foreach (PrototypeMesurande p in Prototypes)
            {
                m.Add(new Mesurande(p.Nom,
                                        (from e in p.Entetes
                                         let indexE = p.Entetes.IndexOf(e)
                                         let indexS = p.Index + indexE
                                         select new { Key = p.Entetes[indexE], Value = Convert.ToDouble(Split[indexS]) }
                                        ).ToDictionary(x => x.Key, x => x.Value)
                        ));
            }


            return m;

        }

        public List<DonneeTheolit> Datas { get; private set; }

        /// <summary>
        /// Retourn eun mesurande selon la colonne DonneeRegex et son nom
        /// </summary>
        /// <param name="Nom"></param>
        /// <param name="DonneeRegex"></param>
        /// <returns></returns>
        public Mesurande TrouveMesurande(string Nom, string DonneeRegex)
        {
            return Datas.FirstOrDefault(d => d.DonneeRegex == DonneeRegex)?.Mesurandes.FirstOrDefault(m => m.Nom == Nom);
        }

        public override string ToString()
        {
            return string.Format("{0}, {1} lignes de mesures", filename, Datas.Count);
        }
    }

0
Whismeril
Messages postés
17648
Date d'inscription
mardi 11 mars 2003
Statut
Modérateur
Dernière intervention
10 août 2022
850
22 mars 2021 à 17:11
Je suis en déplacement jusqu’au début de la semaine prochaine, je n’ai que ma tablette, et elle ne me permet pas de coder.

Quoiqu’il en soit j’aurais besoin du fichier (anonymisé bien sûr) qui fait planter
0
Theolit
Messages postés
40
Date d'inscription
mercredi 18 mars 2020
Statut
Membre
Dernière intervention
20 avril 2021

22 mars 2021 à 17:22
Pas de problème.

J'ai anonymisé un maximum mais dans le doute, je t'envoi le lien par MP ;)

Merci d'avance,
0
Whismeril
Messages postés
17648
Date d'inscription
mardi 11 mars 2003
Statut
Modérateur
Dernière intervention
10 août 2022
850
28 mars 2021 à 19:29
Ok, je viens de tester rapidement.

As tu exécuté en pas à pas et espionné les variable pour voir ce qui se passe?
Je ne pense pas.
J'ai cerné d'où vient le problème en quelques instants, et vu le problème en question si tu l'avais fait toi aussi.

Pour progresser savoir débogger est aussi important que savoir coder (p'tet même plus).

Je t'invite donc par commencer à regarder ce qui se passe et revenir me dire ce que tu constates.
0
Theolit
Messages postés
40
Date d'inscription
mercredi 18 mars 2020
Statut
Membre
Dernière intervention
20 avril 2021

29 mars 2021 à 14:10
J'ai regardé avant de demander mais je pense avoir commenté des lignes en trop pour enlever les hypothèses que je ne voulais pas ...

Avec mon erreur, il me manquait les données dans "prototypes".
J'ai donc décommenté ceci:

 List<PrototypeMesurande> prototypes = new List<PrototypeMesurande>();

            for (i = 0; i < indexMesurandes.Count - 1; i++)
            {
                index1 = indexMesurandes[i];
                int index2 = indexMesurandes[i + 1];
                prototypes.Add(new PrototypeMesurande(metadatas[0][index1], index1, metadatas[9].Skip(index1).Take(index2 - index1).ToArray()));
            }


Sauf que avec ça , il me manque "indesMesurande" qui est présent dans l'hypothèse 2 (que j'ai commenté).
Donc je remet ça :

            //Hypothèse 2, le nom d'un mesurande est toujours précédé de varXXX
            // => on peut extraire l'index de début de chaque mesurande
            List<int> indexMesurandes = metadatas[0].Select((texte, ind) => new { texte, ind }).Where(x => Regex.IsMatch(x.texte, @"^var\d+$")).Select(x => x.ind + 1).ToList();


Et je m'aperçois que 'indexMesurandes' reste à 0.

Bonne piste ? ou je suis loin ?
0
Whismeril
Messages postés
17648
Date d'inscription
mardi 11 mars 2003
Statut
Modérateur
Dernière intervention
10 août 2022
850
29 mars 2021 à 19:06
Oui bonne piste.
Que dit l'énoncé de l'hypothèse 2?
Est applicable à ce fichier?
0
Theolit
Messages postés
40
Date d'inscription
mercredi 18 mars 2020
Statut
Membre
Dernière intervention
20 avril 2021

30 mars 2021 à 13:48
Non, l'hypothèse 2 n'est pas applicable.
Mais si je la commente, j'ai soit un problème avec "prototypes" qui sera vide, soit avec index 1 qui doit prendre la valeur indexMesurandes[i] donc rien..
0
Whismeril
Messages postés
17648
Date d'inscription
mardi 11 mars 2003
Statut
Modérateur
Dernière intervention
10 août 2022
850
30 mars 2021 à 16:36
C’est certain commenter ne peut pas fonctionner.
On a besoin de savoir à quelle colonne commence chaque mesurande pour la suite.

Mais comment faudrait il modifier cette hypothèse pour qu’elle soit applicable à ce fichier et au précédent ?

0
Theolit
Messages postés
40
Date d'inscription
mercredi 18 mars 2020
Statut
Membre
Dernière intervention
20 avril 2021

31 mars 2021 à 09:58
Rappel de l'hypothèse:

List<int> indexMesurandes = metadatas[0].Select((texte, ind) => new { texte, ind }).Where(x => Regex.IsMatch(x.texte, @"^var\d+$")).Select(x => x.ind + 1).ToList();


Il faudrait modifier la partie après Where(...) ?
Je pense remplacer le Regex.IsMatch par quelque chose qui permet de sortir le nom des Mesurandes ?
Peut-être en récupérant la première ligne de "lesLignes" ou de "metadatas".
Mais je ne vois pas trop comment faire pour l'adapter à n'importe quel fichier.
0
Whismeril
Messages postés
17648
Date d'inscription
mardi 11 mars 2003
Statut
Modérateur
Dernière intervention
10 août 2022
850
31 mars 2021 à 15:09
Non ça c’est pas l’hypothèse, c’est le code qui en découle.

L’hypothèse c’est
//Hypothèse 2, le nom d'un mesurande est toujours précédé de varXXX
une précision que je n’avais pas faite c’est XXX représente un nombre à trois chiffres.
  • Est ce que cette hypothèse s’applique au nouveau fichier?
  • Voire s’applique t elle tout court (en gros avais tu modifié les noms dans le premier fichier envoyé)?


0
Theolit
Messages postés
40
Date d'inscription
mercredi 18 mars 2020
Statut
Membre
Dernière intervention
20 avril 2021

31 mars 2021 à 15:58
Non, cette hypothèse ne s'applique pas du tout sur le fichier.
Il n'y a plus de varXXX devant chaque mesurande
0