Remplacement caractères

Résolu
LoicRenard - Modifié le 15 oct. 2022 à 23:19
vb95 Messages postés 3480 Date d'inscription samedi 11 janvier 2014 Statut Non membre Dernière intervention 10 juillet 2024 - 24 oct. 2022 à 23:03

Bonjour,

Je fais un programme de traitement de donnée via une source en CSV

Toutes les colonnes sont séparé par des "," 

Cependant dans certaines colonnes il y a des morceaux de Json donc des "," qui ne devraient pas être compris comme des séparateurs de colonnes.

Comment je peux remplacer ses virgules qui sont entre ses balises [{ "blabla", "blabla"},{ "blabla", "blabla"}] par un ";" afin de ne pas séparer cette partie en deux colonnes.

j'ai pensé à dire que mon CSV sépare les colonnes par un ; mais la source ne vient pas de moi je ne peux donc pas allez vers cette option

Bien à vous

10 réponses

vb95 Messages postés 3480 Date d'inscription samedi 11 janvier 2014 Statut Non membre Dernière intervention 10 juillet 2024 169
23 oct. 2022 à 19:51

Les chiffres avec une virgule sont entre " et " ( je l'ai rajouté ensuite )

Les Regex c'est bien mais ici cela complique les choses . Mieux vaut splitter toute la ligne avec la virgule et ensuite vérifier si le split est à conserver ou non . Cela nous donnera un code plus simple à gérer à mon avis . 


Ah oui bonne idée, Humm je vais essayer de tester un truc 

merci pour le Tuyaux 

Si vous avez un bout de code qui puisse m'aider je reste preneur 

Merci 

0
vb95 Messages postés 3480 Date d'inscription samedi 11 janvier 2014 Statut Non membre Dernière intervention 10 juillet 2024 169
Modifié le 24 oct. 2022 à 05:29

Bonsoir

Bien joué Whismeril

Et voici ma version sans Regex ( une Listbox et une Textbox pour la Form et pour visualiser les 170 colonnes pour la ligne en exemple )

Et elle a l'avantage de garder les virgules qui ne servent pas de séparateur dans le libellé des colonnes .

Et j'obtiens bien les 170 colonnes .

using System.Collections.Generic;
using System;
using System.Windows.Forms;
using System.Linq;

namespace TestRegex
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private string Ligne;

        private void Form1_Load(object sender, System.EventArgs e)
        {
            listBox1.Items.Clear();
            Ligne = "9315c4ce-a467-4d70-98dc-85d5c3b3cfd6,A1T-0032-0,LightPoint,LIMAL/AVENUE-13EME-TIRAILLEURS/,false,,,AVENUE 13EME TIRAILLEURS,LIMAL,1300,Belgique,[],Point,\"[4.570720633,50.69253469]\",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,33,100000,2019-01-01T09:22:00.000Z,Philips-929001573506-SR-C133,,4000,4100,,2019-01-01T09:22:00.000Z,Lightwell,LUXIS SMALL,lum8,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,REW,7704578,15094,M4,1843,,4040,\"0,2 - 0,7\",25 - 77,0,425,Potelet,Aluminium,Enfoui,-,-,-,4X10,EXVB,,211595323,0,0,13 TIRAILLEUR,5820,LED,Philips,,,545108,Voirie,127,LED 24,33,\"9,29002E+11\",SR,C133,40,TCC,2019-01-01T09:22:00.000Z,faux,,,,lum8,Pas de contrôleur,\"[{\"\"key\"\":\"\"1978-01-01T09:22:00.000Z\"\",\"\"value\"\":\"\"HPL 125\"\"}]\",,,,,,,,,,";
            textBox1.Text = Ligne;
        }

        private void Form1_Shown(object sender, EventArgs e)
        {
            string Colonne, FinColonne;
            List<string> ListColonnes = Ligne.Split(',').ToList();
            int IndexLigne = 0;
            while (IndexLigne < ListColonnes.Count)
            {
                Colonne = ListColonnes[IndexLigne];
                if (Colonne == string.Empty || Colonne.StartsWith("\"") == false)
                    // ligne vide ou ligne ne commençant pas par des guillemets
                    IndexLigne++;
                else
                {
                    // la ligne commence par "[ ( début code Json ) la fin du bloc Json est ]"
                    // ligne commençant par " ( valeurs numériques avec virgule ) la fin de ces valeurs numériques est "
                    FinColonne = Colonne.StartsWith("\"[") == true ? "]\"" : "\"";
                    do
                    {
                        Colonne = Colonne + "," + ListColonnes[IndexLigne + 1];
                        ListColonnes.RemoveAt(IndexLigne + 1);
                        if (Colonne.EndsWith(FinColonne))
                        {
                            // fin du bloc Json ou fin du bloc valeurs numériques avec virgule
                            ListColonnes[IndexLigne] = Colonne;
                            IndexLigne++;
                            break;
                        }
                    } while (true);
                }
            }
            listBox1.Items.Clear();
            for (int i = 0; i < ListColonnes.Count; i++)
                listBox1.Items.Add(i.ToString() + "   " + ListColonnes[i]);
        }
    }
}

Whismeril Messages postés 19092 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 14 juillet 2024 658
15 oct. 2022 à 17:19

Bonjour

je pense que les regex sont une bonne solution à ton problème.

Avec cet outil tu pourras soit

  • faire ce que tu as demandé dans ta question 
  • directement parser la ligne de ton csv

Un tuto là https://devstory.net/10795/csharp-regular-expression et ici un site de test http://regexstorm.net/


Bonjour Merci, 

Désolé pour le retard; 

j'ai essayé de faire cela mais je ne trouve pas le moyen pour remplacer juste la virgules ou le parser, 

voici ce que j'ai trouvé: 

string regex = "(\\[.*\\])"; // Regex trouvé 
string output = Regex.Replace(ligne, regex, "Test"); // remplace le regex par test
String[] substrings = output.Split(caractere); // me permet de splitter toutes les virgules 

Voici les problèmes rencontré:

  1. Cela ce remplace pas la virgules mais toute la ligne par "test"

      2. Quand il y a plusieurs [], il ne les prend pas séparément exemple:
Colonne1,Colonne2,[colonne3,colonne3],colonne4,[colonne5,colonne5]
me renvoie:
Colonne1,Colonne2,Test

Donc il prend le premier [ de la ligne et le dernier ] de la ligne 

Comment puis-je résoudre ce problème  ?

0
vb95 Messages postés 3480 Date d'inscription samedi 11 janvier 2014 Statut Non membre Dernière intervention 10 juillet 2024 169
Modifié le 15 oct. 2022 à 17:28

Bonjour

En testant si la virgule est comprise entre { et } . Si c'est le cas cette virgule appartient à un bloc Json sinon c'est un séparateur de colonnes .


Bonjour, avec quel genre de code peut-on faire cela ? 

0

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

Posez votre question
vb95 Messages postés 3480 Date d'inscription samedi 11 janvier 2014 Statut Non membre Dernière intervention 10 juillet 2024 169
Modifié le 23 oct. 2022 à 17:59

Bonjour

Pour mieux cerner votre problème pouvez-vous nous indiquer une ligne réelle de votre fichier source en CSV ?

Autre chose : toutes les lignes de ce fichier CSV ont-elles le même nombre de colonnes ( en principe cela devrait être le cas ) ?

Merci


Bonjour,

Alors voici mon avancement:

Voici un GROS  Brouillons :p

Mais cela fonctionne xD

Ce code est pas optimisé et ne ressemble pas à grand-chose, comment puis-je l'amélioré ?

string regex = "[[^>]";
string regex2 = "[]^>]";
string regex3 = "[,]";
List<int> index = new List<int>();
//string output = Regex.Match(ligne, regex);
string output = ligne;
int i = 0;
char replacement = ';';
                    
foreach (Match match2 in Regex.Matches(ligne, regex2))
{
    index.Add(match2.Index);
}

foreach (Match match in Regex.Matches(ligne, regex))
{
                        

    foreach (Match match3 in Regex.Matches(ligne, regex3))
    {
        if(match3.Index < index[i] && match3.Index > match.Index)
        {
            StringBuilder sb = new StringBuilder(output);
            sb[match3.Index] = replacement;
            output = sb.ToString();
            Console.WriteLine(match3.Value + " a la ligne" + match3.Index + "est passé en ;");
        }
        //Console.WriteLine(match3.Value + match3.Index + " est entre " + match.Index + "et ;" + index[i]);
    }


    i ++;
}
String[] substrings = output.Split(caractere)

Pour le csv il a 170 colonnes et elles ont toutes le même nombre de colonnes (même si elles sont vides)   

9315c4ce-a467-4d70-98dc-85d5c3b3cfd6,A1T-0032-0,LightPoint,LIMAL/AVENUE-13EME-TIRAILLEURS/,false,,,AVENUE 13EME TIRAILLEURS,LIMAL,1300,Belgique,[],Point,"[4.570720633,50.69253469]",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,33,100000,2019-01-01T09:22:00.000Z,Philips-929001573506-SR-C133,,4000,4100,,2019-01-01T09:22:00.000Z,Lightwell,LUXIS SMALL,lum8,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,REW,7704578,15094,M4,1843,,4040,"0,2 - 0,7",25 - 77,0,425,Potelet,Aluminium,Enfoui,-,-,-,4X10,EXVB,,211595323,0,0,13 TIRAILLEUR,5820,LED,Philips,,,545108,Voirie,127,LED 24,33,"9,29002E+11",SR,C133,40,TCC,2019-01-01T09:22:00.000Z,faux,,,,lum8,Pas de contrôleur,"[{""key"":""1978-01-01T09:22:00.000Z"",""value"":""HPL 125""}]",,,,,,,,,,
0
vb95 Messages postés 3480 Date d'inscription samedi 11 janvier 2014 Statut Non membre Dernière intervention 10 juillet 2024 169
Modifié le 23 oct. 2022 à 19:26

Bonsoir

J'ai testé votre code et je me retrouve avec 173 colonnes !

Les balises Json sont bien entre crochets [ ] ?

Pour moi il y en a 2 dans la ligne que vous montrez 

[4.570720633,50.69253469]
[{""key"":""1978-01-01T09:22:00.000Z"",""value"":""HPL 125""}]

Par contre 

"0,2 - 0,7"

doit-il être compté comme un bloc Json ?

Quel est le caractère de début et de fin d'un bloc Json ?   


Bonsoir, 

"0,2 - 0,7"

est deux float donc ne doit pas être séparer en colonnes 

Les bloc json sont dans une balise [ ]

et faut aussi garder les virgules des nombres en virgules

0
vb95 Messages postés 3480 Date d'inscription samedi 11 janvier 2014 Statut Non membre Dernière intervention 10 juillet 2024 169
Modifié le 23 oct. 2022 à 19:42

Ah ok !

Donc il ne faut pas compter les virgules qui sont entre [ et ]  ou entre " et " tout simplement .

C'est très clair comme ceci .


oui et également les chiffres a virgules entre " " que j'ai oublié depuis le début :p 

0
Whismeril Messages postés 19092 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 14 juillet 2024 658
Modifié le 23 oct. 2022 à 22:24

Bonsoir

alors si je comprends bien, y'a quand même un gars qui te fourni un CSV séparé par des virgules avec des nombres décimaux à virgules (et pas à points).

Il est pas un peu c** lui ? C'est juste pas compatible, un CSV séparé par virgules doit obligatoirement avoir des nombres décimaux avec points.

C'est pour ça d'ailleurs que le CSV séparé par points virgules a ensuite été inventé.


Je te propose 2 replace par regex, avec le premier, on met un autre séparateur dans le json (dans l'exemple, ce sera | ) 

(?<="\[)(.*?),(.*?)(?=\]")

Et remplacement

$1|$2

Voir exemple ici


Dans le second, on met un point pour les nombres décimaux (pour l'exemple, j'ai pris le texte modifié par la regex précédente)

(?<=,")([\d -.]*?),(.*?)(?=",)

Et remplacement  

$1.$2

Voir exemple ici

On constate que la première passe ne capture pas le 0,7, c'est parce qu'il fait partie de $2 de la capture qui prend le 0,2.

L'option la plus simple est de faire ce remplacement 2 fois ( exemple, une fois les 2 premières captures changées ici )

On peut aussi trouver une regex qui s'adapte aux 2 cas, mais, il faudra gérer les remplacements "à la main", comme tu l'as fait plus haut, en fonction des groupes qui seront capturés.

(?<=,")([\d.]*?),(?:(\d+ - \d+?),(\d)|(.*?))(?=",)

Et donc pas de remplacement directement en regex.

Exemple ici 

Regarde la partie "Match information", tu verras que pour "0.2 - 0,7", il y a les groupes 1, 2 et 3, alors que pour "9,29002E+11" il y a les groupes 1 et 4
Ensuite, tu peux faire un split avec les virgules.


Pourquoi dans ce sens, parce que là, les 2 regex replaces sont simples, et qu'il peut y avoir des décimaux dans le json, alors il faut commencer par lui.

Alors que si on veut remplacer les virgules de colonnes par des points virgules, c'est beaucoup plus complexe.


Whismeril Messages postés 19092 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 14 juillet 2024 658
23 oct. 2022 à 22:51
            string[] lignes = File.ReadAllLines("test.csv");

            string pattern1 = @"(?<=""\[)(.*?),(.*?)(?=\]"")";
            string pattern2 = @"(?<=,"")([\d -.]*?),(.*?)(?="",)";

            foreach (string ligne in lignes)
            {
                //on remplace le , dans les json
                string action1 = Regex.Replace(ligne, pattern1, "$1|$2");


                //on remplace les , dans les nombres
                string action2 = Regex.Replace(action1, pattern2, "$1.$2");

                //on remplace les , dans les nombres, une 2eme fois
                string action3 = Regex.Replace(action2, pattern2, "$1.$2");

                string[] valeurs = action3.Split(",");
            }
1
LoicRenard > Whismeril Messages postés 19092 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 14 juillet 2024
24 oct. 2022 à 20:21

Bonsoir Whismeril,

c'est un CSV Exporter d'une application web et je pense qu'il a choisis la méthodes de csv a virgules car il doit être compatible a de multiple plateforme.

Merci pour ton aides 

je vais tester cela :)

pour le moment je sépare tout et je réassocie les colonnes si elles remplissent des conditions

mais cela fonctionne 

Bonne soirée

0
Whismeril Messages postés 19092 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 14 juillet 2024 658 > LoicRenard
24 oct. 2022 à 21:05

Le CSV à virgule est cross plateforme oui, mais les nombres décimaux à virgules non, c'est Windows français uniquement.

Le standard pour les décimaux, c'est le point. D'ailleurs, il y a aussi des décimaux à points dans la ligne (dans un json et pour les datations), tu vois : rien de cohérent.

Donc, il a fait des choix contradictoires.

Et en plus, il y a des virgules dans le json.


En ce qui me concerne, j'aurais choisi, CSV à point virgules, décimaux à point et json à virgules, comme ça aucun doutes possibles.

Mais bon.


Ton premier code est couteux en RAM et en temps, car tu analyses plusieurs fois la ligne, tu fais des boucles dans des boucles, tu stockes des index, tu joues avec stringbuilder et tu fais des if, il y a potentiellement un risque de stackoverflow.

Celui de VB95 en temps, parce qu'il fait des boucles dans des boucles agrémentées de if, il y a potentiellement un risque de stackoverflow.

Sur une ligne, ça n'a pas d'incidence, mais si ton fichier en contient plusieurs milliers, ça va se faire sentir

Mon code, partant du principe que les regex sont vendues pour être optimisées en temps et en mémoire, devrait, et j'insiste sur le conditionnel, devrait donc être moins gourmand en RAM et en temps et le risque de stackoverflow devrait être moindre.

Car je n'ai passé que 3 regex par ligne et elles sont simples (plus c'est compliqué et plus ça demande de ressource pour l'analyse). Cependant, avec une seule ligne exemple, impossible de garantir une compatibilité sur l'ensemble de tes fichiers.

J'ai réfléchi, entre temps, à une généralisation (toujours sur la base d'une unique ligne...), je vais faire des tests et poster si c'est concluant.

0
LoicRenard > Whismeril Messages postés 19092 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 14 juillet 2024
24 oct. 2022 à 21:11

Si vous le souhaitez, je peux vous envoyer une copie du csv en privé,

si cela peut vous aidez.

merci

0
Whismeril Messages postés 19092 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 14 juillet 2024 658 > LoicRenard
24 oct. 2022 à 22:00

Tu peux commencer par essayer ça

            string[] lignes = File.ReadAllLines("test.csv");

            string pattern1 = @"\[.*?\]";
            string pattern2 = @"""(\d+,\d+( - \d+,\d+)+|\d,\d+E[+-]\d\d)""";

            List<string[]> resultat = new List<string[]>();

            foreach (string ligne in lignes)
            {
                //on remplace le , dans les json par | 
                string action1 = Regex.Replace(ligne, pattern1, delegate (Match m) 
                {
                    return m.Value.Replace(',','|') ; // s'il y a plusieurs , dans le json, elles seront toutes remplacées
                }
                );


                //on remplace les , dans les nombres "0,2 - 0,7" et "9,29002E+11" sont capturés, mais "0,2 - 0,7 - 12,13" le serait ausi
                string action2 = Regex.Replace(action1, pattern2, delegate (Match m)
                {
                    return m.Value.Replace(',', '.'); // s'il y a plusieurs , dans les décimaux, elles seront toutes remplacées
                }
                );

                //on remplace toutes les virgules restantes par des points virgules
                string action3 = action2.Replace(',',';');

                //on remet les virgules dans les json mais pas dans les décimaux
                string action4 = action3.Replace('|', ',');

                //On splite la ligne par les ;
                resultat.Add(action4.Split(';'));

            }

J'ai utilisé une possibilité offerte par C# de construire soi-même la chaine de remplacement avec un délégué.

Cela me permet d'étendre le remplacement dans les json à plusieurs virgules au cas où il pourrait y en avoir.

De même, dans les champs du type "0,2 - 0,7" les 2 virgules (ou 3, ou 4, car j'ai ouvert la regex à "0,2 - 0,7 - 1.123 - 12.12" etc.. seront remplacées en une seule fois par des points, comme ça c'est cohérent du standard international.

Ensuite, toutes les virgules restantes sont remplacées par des ; et je remets les , dans les json.

Ainsi, on a une ligne normée et cohérente.

Enfin je la splite sur les ; et je stocke le résultat dans une liste de tableaux de string.



Si ça fonctionne avec ton fichier, pas besoin de plus.

Si ça ne fonctionne pas, alors oui tu peux mettre le fichier sur un serveur de partage et m'envoyer le lien par MP.

Mais il faut me prévenir d'avance, car par défaut, j'ai bloqué les MP.

0

Bonsoir a tous,

et merci pour votre aide,

j'ai adopté la méthode de vb95

merci à tous 

vb95 Messages postés 3480 Date d'inscription samedi 11 janvier 2014 Statut Non membre Dernière intervention 10 juillet 2024 169
Modifié le 25 oct. 2022 à 01:57

Bonsoir

La solution de Whismeril respecte plus le "standard international" au niveau des fichiers CSV alors que ma solution ne modifie nullement les colonnes du fichier CSV d'origine .

Si le sujet te semble traité prière de le "mettre en résolu" avec les 3 ... de ton premier message .

Merci


Rejoignez-nous