web-dev-qa-db-fra.com

scinder une chaîne séparée par des virgules avec des chaînes entre guillemets et entre guillemets

J'ai la chaîne suivante, séparée par des virgules, que je dois diviser. Le problème est qu'une partie du contenu est entre guillemets et contient des virgules qui ne doivent pas être utilisées dans la scission ...

Chaîne:

111,222,"33,44,55",666,"77,88","99"

Je veux la sortie:

111  
222  
33,44,55  
666  
77,88  
99  

J'ai essayé ceci: 

(?:,?)((?<=")[^"]+(?=")|[^",]+)   

Mais il lit la virgule entre "77,88", "99" comme un hit et j'obtiens la sortie suivante: 

111  
222  
33,44,55  
666  
77,88  
,  
99  

Quelqu'un peut-il m'aider? Je manque de temps ...:) /Peter

44
Peter Norlén

Selon vos besoins, vous ne pourrez peut-être pas utiliser un analyseur CSV et voudrez peut-être réinventer la roue!

Vous pouvez le faire avec un regex simple

(?:^|,)(\"(?:[^\"]+|\"\")*\"|[^,]*)

Cela fera ce qui suit:

(?:^|,) = Expression correspondante "Début de la ligne ou de la chaîne ,"

(\"(?:[^\"]+|\"\")*\"|[^,]*) = Un groupe de capture numéroté, cela permettra de choisir entre 2 alternatives:

  1. des choses entre guillemets
  2. des choses entre des virgules

Cela devrait vous donner le résultat que vous recherchez.

Exemple de code en C #

 static Regex csvSplit = new Regex("(?:^|,)(\"(?:[^\"]+|\"\")*\"|[^,]*)", RegexOptions.Compiled);

public static string[] SplitCSV(string input)
{

  List<string> list = new List<string>();
  string curr = null;
  foreach (Match match in csvSplit.Matches(input))
  {        
    curr = match.Value;
    if (0 == curr.Length)
    {
      list.Add("");
    }

    list.Add(curr.TrimStart(','));
  }

  return list.ToArray();
}

private void button1_Click(object sender, RoutedEventArgs e)
{
    Console.WriteLine(SplitCSV("111,222,\"33,44,55\",666,\"77,88\",\"99\""));
}

Avertissement Selon le commentaire de @ MrE - si un caractère de nouvelle ligne non autorisé apparaît dans un fichier CSV mal formé et que vous vous retrouvez avec une chaîne inégale ("chaîne"), vous obtenez un retour en arrière catastrophique ( https: // www. regular-expressions.info/catastrophic.html ) dans votre regex et votre système va probablement planter (comme notre système de production l'a fait). Peut facilement être répliqué dans Visual Studio et, comme je l'ai découvert, va le planter. catch ne va pas piéger ce problème non plus.

Tu devrais utiliser:

(?:^|,)(\"(?:[^\"])*\"|[^,]*)

au lieu

79
jimplode

j'aime beaucoup la réponse de jimplode, mais je pense qu'une version avec rendement de retour est un peu plus utile, alors la voici:

public IEnumerable<string> SplitCSV(string input)
{
    Regex csvSplit = new Regex("(?:^|,)(\"(?:[^\"]+|\"\")*\"|[^,]*)", RegexOptions.Compiled);

    foreach (Match match in csvSplit.Matches(input))
    {
        yield return match.Value.TrimStart(',');
    }
}

Peut-être qu'il est encore plus utile de l'avoir comme une méthode d'extension:

public static class StringHelper
{
    public static IEnumerable<string> SplitCSV(this string input)
    {
        Regex csvSplit = new Regex("(?:^|,)(\"(?:[^\"]+|\"\")*\"|[^,]*)", RegexOptions.Compiled);

        foreach (Match match in csvSplit.Matches(input))
        {
            yield return match.Value.TrimStart(',');
        }
    }
}
13
qqbenq

Cette expression régulière fonctionne sans qu'il soit nécessaire de parcourir en boucle les valeurs et TrimStart(','), comme dans la réponse acceptée:

((?<=\")[^\"]*(?=\"(,|$)+)|(?<=,|^)[^,\"]*(?=,|$))

Voici l'implémentation en C #:

string values = "111,222,\"33,44,55\",666,\"77,88\",\"99\"";

MatchCollection matches = new Regex("((?<=\")[^\"]*(?=\"(,|$)+)|(?<=,|^)[^,\"]*(?=,|$))").Matches(values);

foreach (var match in matches)
{
    Console.WriteLine(match);
}

Les sorties

111  
222  
33,44,55  
666  
77,88  
99  
4
Chris Schiffhauer

Aucune de ces réponses ne fonctionne lorsque la chaîne comporte une virgule entre guillemets, comme dans "value, 1", ou des guillemets d'échappement, comme dans "value ""1""", qui sont CSV valide, qui doivent être analysés respectivement comme value, 1 et value "1".

Cela fonctionnera également avec le format délimité par des tabulations si vous passez un onglet au lieu d’une virgule comme délimiteur.

public static IEnumerable<string> SplitRow(string row, char delimiter = ',')
{
    var currentString = new StringBuilder();
    var inQuotes = false;
    var quoteIsEscaped = false; //Store when a quote has been escaped.
    row = string.Format("{0}{1}", row, delimiter); //We add new cells at the delimiter, so append one for the parser.
    foreach (var character in row.Select((val, index) => new {val, index}))
    {
        if (character.val == delimiter) //We hit a delimiter character...
        {
            if (!inQuotes) //Are we inside quotes? If not, we've hit the end of a cell value.
            {
                Console.WriteLine(currentString);
                yield return currentString.ToString();
                currentString.Clear();
            }
            else
            {
                currentString.Append(character.val);
            }
        } else {
            if (character.val != ' ')
            {
                if(character.val == '"') //If we've hit a quote character...
                {
                    if(character.val == '\"' && inQuotes) //Does it appear to be a closing quote?
                    {
                        if (row[character.index + 1] == character.val) //If the character afterwards is also a quote, this is to escape that (not a closing quote).
                        {
                            quoteIsEscaped = true; //Flag that we are escaped for the next character. Don't add the escaping quote.
                        }
                        else if (quoteIsEscaped)
                        {
                            quoteIsEscaped = false; //This is an escaped quote. Add it and revert quoteIsEscaped to false.
                            currentString.Append(character.val);
                        }
                        else
                        {
                            inQuotes = false;
                        }
                    }
                    else
                    {
                        if (!inQuotes)
                        {
                            inQuotes = true;
                        }
                        else
                        {
                            currentString.Append(character.val); //...It's a quote inside a quote.
                        }
                    }
                }
                else
                {
                    currentString.Append(character.val);
                }
            }
            else
            {
                if (!string.IsNullOrWhiteSpace(currentString.ToString())) //Append only if not new cell
                {
                    currentString.Append(character.val);
                }
            }
        }
    }
}
3
Chad Hedgcock

Rapide et facile:

    public static string[] SplitCsv(string line)
    {
        List<string> result = new List<string>();
        StringBuilder currentStr = new StringBuilder("");
        bool inQuotes = false;
        for (int i = 0; i < line.Length; i++) // For each character
        {
            if (line[i] == '\"') // Quotes are closing or opening
                inQuotes = !inQuotes;
            else if (line[i] == ',') // Comma
            {
                if (!inQuotes) // If not in quotes, end of current string, add it to result
                {
                    result.Add(currentStr.ToString());
                    currentStr.Clear();
                }
                else
                    currentStr.Append(line[i]); // If in quotes, just add it 
            }
            else // Add any other character to current string
                currentStr.Append(line[i]); 
        }
        result.Add(currentStr.ToString());
        return result.ToArray(); // Return array of all strings
    }

Avec cette chaîne en entrée:

 111,222,"33,44,55",666,"77,88","99"

Il reviendra:

111  
222  
33,44,55  
666  
77,88  
99  
3
Antoine

Avec des mises à jour mineures apportées à la fonction fournie par "Chad Hedgcock".

Les mises à jour sont sur: 

Ligne 26: character.val == '\ "' - Cela ne peut jamais être vrai en raison de la vérification effectuée sur la ligne 24. i.e. character.val == '"'

Ligne 28: if (row [character.index + 1] == character.val) ajouté! QuoteIsEscaped pour échapper à 3 guillemets consécutifs.

public static IEnumerable<string> SplitRow(string row, char delimiter = ',')
{
var currentString = new StringBuilder();
var inQuotes = false;
var quoteIsEscaped = false; //Store when a quote has been escaped.
row = string.Format("{0}{1}", row, delimiter); //We add new cells at the delimiter, so append one for the parser.
foreach (var character in row.Select((val, index) => new {val, index}))
{
    if (character.val == delimiter) //We hit a delimiter character...
    {
        if (!inQuotes) //Are we inside quotes? If not, we've hit the end of a cell value.
        {
            //Console.WriteLine(currentString);
            yield return currentString.ToString();
            currentString.Clear();
        }
        else
        {
            currentString.Append(character.val);
        }
    } else {
        if (character.val != ' ')
        {
            if(character.val == '"') //If we've hit a quote character...
            {
                if(character.val == '"' && inQuotes) //Does it appear to be a closing quote?
                {
                    if (row[character.index + 1] == character.val && !quoteIsEscaped) //If the character afterwards is also a quote, this is to escape that (not a closing quote).
                    {
                        quoteIsEscaped = true; //Flag that we are escaped for the next character. Don't add the escaping quote.
                    }
                    else if (quoteIsEscaped)
                    {
                        quoteIsEscaped = false; //This is an escaped quote. Add it and revert quoteIsEscaped to false.
                        currentString.Append(character.val);
                    }
                    else
                    {
                        inQuotes = false;
                    }
                }
                else
                {
                    if (!inQuotes)
                    {
                        inQuotes = true;
                    }
                    else
                    {
                        currentString.Append(character.val); //...It's a quote inside a quote.
                    }
                }
            }
            else
            {
                currentString.Append(character.val);
            }
        }
        else
        {
            if (!string.IsNullOrWhiteSpace(currentString.ToString())) //Append only if not new cell
            {
                currentString.Append(character.val);
            }
        }
    }
}

}

3
Shiva

Pour la réponse de Jay, si vous utilisez un 2e booléen, vous pouvez avoir des guillemets imbriqués entre guillemets simples et vice-versa.

    private string[] splitString(string stringToSplit)
{
    char[] characters = stringToSplit.ToCharArray();
    List<string> returnValueList = new List<string>();
    string tempString = "";
    bool blockUntilEndQuote = false;
    bool blockUntilEndQuote2 = false;
    int characterCount = 0;
    foreach (char character in characters)
    {
        characterCount = characterCount + 1;

        if (character == '"' && !blockUntilEndQuote2)
        {
            if (blockUntilEndQuote == false)
            {
                blockUntilEndQuote = true;
            }
            else if (blockUntilEndQuote == true)
            {
                blockUntilEndQuote = false;
            }
        }
        if (character == '\'' && !blockUntilEndQuote)
        {
            if (blockUntilEndQuote2 == false)
            {
                blockUntilEndQuote2 = true;
            }
            else if (blockUntilEndQuote2 == true)
            {
                blockUntilEndQuote2 = false;
            }
        }

        if (character != ',')
        {
            tempString = tempString + character;
        }
        else if (character == ',' && (blockUntilEndQuote == true || blockUntilEndQuote2 == true))
        {
            tempString = tempString + character;
        }
        else
        {
            returnValueList.Add(tempString);
            tempString = "";
        }

        if (characterCount == characters.Length)
        {
            returnValueList.Add(tempString);
            tempString = "";
        }
    }

    string[] returnValue = returnValueList.ToArray();
    return returnValue;
}
2
And Wan

Ne réinventez pas un analyseur CSV, essayez FileHelpers .

2
Darin Dimitrov

Essaye ça:

       string s = @"111,222,""33,44,55"",666,""77,88"",""99""";

       List<string> result = new List<string>();

       var splitted = s.Split('"').ToList<string>();
       splitted.RemoveAll(x => x == ",");
       foreach (var it in splitted)
       {
           if (it.StartsWith(",") || it.EndsWith(","))
           {
               var tmp = it.TrimEnd(',').TrimStart(',');
               result.AddRange(tmp.Split(','));
           }
           else
           {
               if(!string.IsNullOrEmpty(it)) result.Add(it);
           }
      }
       //Results:

       foreach (var it in result)
       {
           Console.WriteLine(it);
       }
2
nan

J'avais besoin de quelque chose d'un peu plus robuste, alors j'ai pris à partir d'ici et créé ceci ... Cette solution est un peu moins élégante et un peu plus verbeuse, mais lors de mes tests (avec un échantillon de 1 000 000 lignes), j'ai trouvé qu'il à 3 fois plus vite. De plus, il gère les citations incorporées non échappées. J'ai utilisé un délimiteur de chaîne et des qualificateurs au lieu de caractères en raison des exigences de ma solution. J'ai trouvé plus difficile que prévu de trouver un bon analyseur générique CSV. J'espère donc que cet algorithme d'analyse peut aider quelqu'un. 

    public static string[] SplitRow(string record, string delimiter, string qualifier, bool trimData)
    {
        // In-Line for example, but I implemented as string extender in production code
        Func <string, int, int> IndexOfNextNonWhiteSpaceChar = delegate (string source, int startIndex)
        {
            if (startIndex >= 0)
            {
                if (source != null)
                {
                    for (int i = startIndex; i < source.Length; i++)
                    {
                        if (!char.IsWhiteSpace(source[i]))
                        {
                            return i;
                        }
                    }
                }
            }

            return -1;
        };

        var results = new List<string>();
        var result = new StringBuilder();
        var inQualifier = false;
        var inField = false;

        // We add new columns at the delimiter, so append one for the parser.
        var row = $"{record}{delimiter}";

        for (var idx = 0; idx < row.Length; idx++)
        {
            // A delimiter character...
            if (row[idx]== delimiter[0])
            {
                // Are we inside qualifier? If not, we've hit the end of a column value.
                if (!inQualifier)
                {
                    results.Add(trimData ? result.ToString().Trim() : result.ToString());
                    result.Clear();
                    inField = false;
                }
                else
                {
                    result.Append(row[idx]);
                }
            }

            // NOT a delimiter character...
            else
            {
                // ...Not a space character
                if (row[idx] != ' ')
                {
                    // A qualifier character...
                    if (row[idx] == qualifier[0])
                    {
                        // Qualifier is closing qualifier...
                        if (inQualifier && row[IndexOfNextNonWhiteSpaceChar(row, idx + 1)] == delimiter[0])
                        {
                            inQualifier = false;
                            continue;
                        }

                        else
                        {
                            // ...Qualifier is opening qualifier
                            if (!inQualifier)
                            {
                                inQualifier = true;
                            }

                            // ...It's a qualifier inside a qualifier.
                            else
                            {
                                inField = true;
                                result.Append(row[idx]);
                            }
                        }
                    }

                    // Not a qualifier character...
                    else
                    {
                        result.Append(row[idx]);
                        inField = true;
                    }
                }

                // ...A space character
                else
                {
                    if (inQualifier || inField)
                    {
                        result.Append(row[idx]);
                    }
                }
            }
        }

        return results.ToArray<string>();
    }

Un code de test:

        //var input = "111,222,\"33,44,55\",666,\"77,88\",\"99\"";

        var input =
            "111, 222, \"99\",\"33,44,55\" ,      \"666 \"mark of a man\"\", \" spaces \"77,88\"   \"";

        Console.WriteLine("Split with trim");
        Console.WriteLine("---------------");
        var result = SplitRow(input, ",", "\"", true);
        foreach (var r in result)
        {
            Console.WriteLine(r);
        }
        Console.WriteLine("");

        // Split 2
        Console.WriteLine("Split with no trim");
        Console.WriteLine("------------------");
        var result2 = SplitRow(input, ",", "\"", false);
        foreach (var r in result2)
        {
            Console.WriteLine(r);
        }
        Console.WriteLine("");

        // Time Trial 1
        Console.WriteLine("Experimental Process (1,000,000) iterations");
        Console.WriteLine("-------------------------------------------");
        watch = Stopwatch.StartNew();
        for (var i = 0; i < 1000000; i++)
        {
            var x1 = SplitRow(input, ",", "\"", false);
        }
        watch.Stop();
        elapsedMs = watch.ElapsedMilliseconds;
        Console.WriteLine($"Total Process Time: {string.Format("{0:0.###}", elapsedMs / 1000.0)} Seconds");
        Console.WriteLine("");

Résultats

Split with trim
---------------
111
222
99
33,44,55
666 "mark of a man"
spaces "77,88"

Split with no trim
------------------
111
222
99
33,44,55
666 "mark of a man"
 spaces "77,88"

Original Process (1,000,000) iterations
-------------------------------
Total Process Time: 7.538 Seconds

Experimental Process (1,000,000) iterations
--------------------------------------------
Total Process Time: 3.363 Seconds
1
Sam Jazz

Actuellement, j'utilise l'expression régulière suivante:

    public static Regex regexCSVSplit = new Regex(@"(?x:(
        (?<FULL>
        (^|[,;\t\r\n])\s*
        ( (?<CODAT> (?<CO>[""'])(?<DAT>([^,;\t\r\n]|(?<!\k<CO>\s*)[,;\t\r\n])*)\k<CO>) |
          (?<CODAT> (?<DAT> [^""',;\s\r\n]* )) )
        (?=\s*([,;\t\r\n]|$))
        ) |
        (?<FULL>
        (^|[\s\t\r\n])
        ( (?<CODAT> (?<CO>[""'])(?<DAT> [^""',;\s\t\r\n]* )\k<CO>) |
        (?<CODAT> (?<DAT> [^""',;\s\t\r\n]* )) )
        (?=[,;\s\t\r\n]|$))
        ))", RegexOptions.Compiled);

Cette solution peut aussi traiter des cas assez chaotiques, comme ci-dessous:  enter image description here

Voici comment introduire le résultat dans un tableau:

    var data = regexCSVSplit.Matches(line_to_process).Cast<Match>().Select(x => x.Groups["DAT"].Value).ToArray();

Voir cet exemple en action ICI

1
SchLx

Je sais que je suis un peu en retard, mais pour les recherches, voici comment j'ai fait ce que vous demandez à propos de C sharp

private string[] splitString(string stringToSplit)
    {
        char[] characters = stringToSplit.ToCharArray();
        List<string> returnValueList = new List<string>();
        string tempString = "";
        bool blockUntilEndQuote = false;
        int characterCount = 0;
        foreach (char character in characters)
        {
            characterCount = characterCount + 1;

            if (character == '"')
            {
                if (blockUntilEndQuote == false)
                {
                    blockUntilEndQuote = true;
                }
                else if (blockUntilEndQuote == true)
                {
                    blockUntilEndQuote = false;
                }
            }

            if (character != ',')
            {
                tempString = tempString + character;
            }
            else if (character == ',' && blockUntilEndQuote == true)
            {
                tempString = tempString + character;
            }
            else
            {
                returnValueList.Add(tempString);
                tempString = "";
            }

            if (characterCount == characters.Length)
            {
                returnValueList.Add(tempString);
                tempString = "";
            }
        }

        string[] returnValue = returnValueList.ToArray();
        return returnValue;
    }
1
Bbb

Voici mon implémentation la plus rapide basée sur la manipulation de pointeur de chaîne brute:

string[] FastSplit(string sText, char? cSeparator = null, char? cQuotes = null)
    {            
        string[] oTokens;

        if (null == cSeparator)
        {
            cSeparator = DEFAULT_PARSEFIELDS_SEPARATOR;
        }

        if (null == cQuotes)
        {
            cQuotes = DEFAULT_PARSEFIELDS_QUOTE;
        }

        unsafe
        {
            fixed (char* lpText = sText)
            {
                #region Fast array estimatation

                char* lpCurrent      = lpText;                    
                int   nEstimatedSize = 0;

                while (0 != *lpCurrent)
                {
                    if (cSeparator == *lpCurrent)
                    {
                        nEstimatedSize++;
                    }

                    lpCurrent++;
                }

                nEstimatedSize++; // Add EOL char(s)
                string[] oEstimatedTokens = new string[nEstimatedSize];

                #endregion

                #region Parsing

                char[] oBuffer = new char[sText.Length];
                int    nIndex  = 0;
                int    nTokens = 0;

                lpCurrent      = lpText;

                while (0 != *lpCurrent)
                {
                    if (cQuotes == *lpCurrent)
                    {
                        // Quotes parsing

                        lpCurrent++; // Skip quote
                        nIndex = 0;  // Reset buffer

                        while (
                               (0       != *lpCurrent)
                            && (cQuotes != *lpCurrent)
                        )
                        {
                            oBuffer[nIndex] = *lpCurrent; // Store char

                            lpCurrent++; // Move source cursor
                            nIndex++;    // Move target cursor
                        }

                    } 
                    else if (cSeparator == *lpCurrent)
                    {
                        // Separator char parsing

                        oEstimatedTokens[nTokens++] = new string(oBuffer, 0, nIndex); // Store token
                        nIndex                      = 0;                              // Skip separator and Reset buffer
                    }
                    else
                    {
                        // Content parsing

                        oBuffer[nIndex] = *lpCurrent; // Store char
                        nIndex++;                     // Move target cursor
                    }

                    lpCurrent++; // Move source cursor
                }

                // Recover pending buffer

                if (nIndex > 0)
                {
                    // Store token

                    oEstimatedTokens[nTokens++] = new string(oBuffer, 0, nIndex);
                }

                // Build final tokens list

                if (nTokens == nEstimatedSize)
                {
                    oTokens = oEstimatedTokens;
                }
                else
                {
                    oTokens = new string[nTokens];
                    Array.Copy(oEstimatedTokens, 0, oTokens, 0, nTokens);
                }

                #endregion
            }
        }

        // Epilogue            

        return oTokens;
    }
0
Antonio Petricca

Essaye ça

private string[] GetCommaSeperatedWords(string sep, string line)
    {
        List<string> list = new List<string>();
        StringBuilder Word = new StringBuilder();
        int doubleQuoteCount = 0;
        for (int i = 0; i < line.Length; i++)
        {
            string chr = line[i].ToString();
            if (chr == "\"")
            {
                if (doubleQuoteCount == 0)
                    doubleQuoteCount++;
                else
                    doubleQuoteCount--;

                continue;
            }
            if (chr == sep && doubleQuoteCount == 0)
            {
                list.Add(Word.ToString());
                Word = new StringBuilder();
                continue;
            }
            Word.Append(chr);
        }

        list.Add(Word.ToString());

        return list.ToArray();
    }
0
Krishna

C’est la réponse de Chad qui a été réécrite avec une logique d’état. Sa réponse a échoué pour moi lorsqu'il est tombé sur """BRAD""" en tant que champ. Cela devrait renvoyer "BRAD" mais il vient de manger tous les champs restants. Lorsque j'ai essayé de le déboguer, j'ai fini par le réécrire sous forme de logique d'état:

enum SplitState { s_begin, s_infield, s_inquotefield, s_foundquoteinfield };
public static IEnumerable<string> SplitRow(string row, char delimiter = ',')
{
    var currentString = new StringBuilder();
    SplitState state = SplitState.s_begin;
    row = string.Format("{0}{1}", row, delimiter); //We add new cells at the delimiter, so append one for the parser.
    foreach (var character in row.Select((val, index) => new { val, index }))
    {
        //Console.WriteLine("character = " + character.val + " state = " + state);
        switch (state)
        {
            case SplitState.s_begin:
                if (character.val == delimiter)
                {
                    /* empty field */
                    yield return currentString.ToString();
                    currentString.Clear();
                } else if (character.val == '"')
                {
                    state = SplitState.s_inquotefield;
                } else
                {
                    currentString.Append(character.val);
                    state = SplitState.s_infield;
                }
                break;
            case SplitState.s_infield:
                if (character.val == delimiter)
                {
                    /* field with data */
                    yield return currentString.ToString();
                    state = SplitState.s_begin;
                    currentString.Clear();
                } else
                {
                    currentString.Append(character.val);
                }
                break;
            case SplitState.s_inquotefield:
                if (character.val == '"')
                {
                    // could be end of field, or escaped quote.
                    state = SplitState.s_foundquoteinfield;
                } else
                {
                    currentString.Append(character.val);
                }
                break;
            case SplitState.s_foundquoteinfield:
                if (character.val == '"')
                {
                    // found escaped quote.
                    currentString.Append(character.val);
                    state = SplitState.s_inquotefield;
                }
                else if (character.val == delimiter)
                {
                    // must have been last quote so we must find delimiter
                    yield return currentString.ToString();
                    state = SplitState.s_begin;
                    currentString.Clear();
                }
                else
                {
                    throw new Exception("Quoted field not terminated.");
                }
                break;
            default:
                throw new Exception("unknown state:" + state);
        }
    }
    //Console.WriteLine("currentstring = " + currentString.ToString());
}

C'est beaucoup plus de lignes de code que les autres solutions, mais il est facile de modifier pour ajouter des cas Edge.

0
Michael Potter

Une fois, j'ai dû faire quelque chose de similaire et à la fin je me suis retrouvé coincé avec les expressions régulières. L’incapacité de Regex d’avoir cet état le rend assez compliqué - j’ai juste fini par écrire un petit analyseur simple .

Si vous effectuez une analyse CSV, vous devriez simplement vous en tenir à en utilisant un analyseur CSV - ne réinventez pas la roue.

0
Jaco Pretorius