Recherche de Regex pour trouver des nouvelles lignes entre guillemets dans une grande chaîne (pour C#)
Question
J'ai une grosse chaîne (appelons-la un fichier CSV, même si ce n'en est pas réellement un, ce sera juste plus facile pour le moment) que je dois analyser en code C#.
La première étape du processus d'analyse divise le fichier en lignes individuelles en utilisant simplement un StreamReader
objet et appel ReadLine
jusqu'à ce que ce soit à travers le fichier.Cependant, n'importe quelle ligne donnée peut contenir un littéral entre guillemets simples avec des nouvelles lignes intégrées.Je dois trouver ces nouvelles lignes et les convertir temporairement en un autre type de jeton ou de séquence d'échappement jusqu'à ce que j'aie divisé le fichier en un tableau de lignes... puis je peux les modifier.
Exemples de données d'entrée :
1,2,10,99,'Some text without a newline', true, false, 90
2,1,11,98,'This text has an embedded newline
and continues here', true, true, 90
Je pourrais écrire tout le code C# nécessaire pour ce faire en utilisant string.IndexOf
pour trouver les sections citées et y rechercher des nouvelles lignes, mais je pense qu'une Regex pourrait être un meilleur choix (c'est-à-dire maintenant j'ai deux problèmes)
La solution
Puisqu'il ne s'agit pas d'un véritable fichier CSV, a-t-il une sorte de schéma ?
D'après votre exemple, il semble que vous ayez :int, int, int, int, chaîne, bool, bool, int
Avec cela constituant votre enregistrement/objet.
En supposant que vos données sont bien formées (je ne connais pas suffisamment votre source pour savoir dans quelle mesure cette hypothèse est valable) ;vous pourriez:
- Lisez votre ligne.
- Utilisez une machine à états pour analyser vos données.
- Si votre ligne se termine et que vous analysez une chaîne, lisez la ligne suivante... et continuez l'analyse.
J'éviterais d'utiliser une regex si possible.
Autres conseils
Les machines à états permettant d'effectuer un tel travail sont simplifiées à l'aide des itérateurs C# 2.0.Voici, espérons-le, le dernier analyseur CSV que j'écrirai.L'ensemble du fichier est traité comme un groupe énumérable de chaînes énumérables, c'est-à-direrangées colonnes.IEnumerable est génial car il peut ensuite être traité par les opérateurs LINQ.
public class CsvParser
{
public char FieldDelimiter { get; set; }
public CsvParser()
: this(',')
{
}
public CsvParser(char fieldDelimiter)
{
FieldDelimiter = fieldDelimiter;
}
public IEnumerable<IEnumerable<string>> Parse(string text)
{
return Parse(new StringReader(text));
}
public IEnumerable<IEnumerable<string>> Parse(TextReader reader)
{
while (reader.Peek() != -1)
yield return parseLine(reader);
}
IEnumerable<string> parseLine(TextReader reader)
{
bool insideQuotes = false;
StringBuilder item = new StringBuilder();
while (reader.Peek() != -1)
{
char ch = (char)reader.Read();
char? nextCh = reader.Peek() > -1 ? (char)reader.Peek() : (char?)null;
if (!insideQuotes && ch == FieldDelimiter)
{
yield return item.ToString();
item.Length = 0;
}
else if (!insideQuotes && ch == '\r' && nextCh == '\n') //CRLF
{
reader.Read(); // skip LF
break;
}
else if (!insideQuotes && ch == '\n') //LF for *nix-style line endings
break;
else if (ch == '"' && nextCh == '"') // escaped quotes ""
{
item.Append('"');
reader.Read(); // skip next "
}
else if (ch == '"')
insideQuotes = !insideQuotes;
else
item.Append(ch);
}
// last one
yield return item.ToString();
}
}
Notez que le fichier est lu caractère par caractère, le code décidant quand les nouvelles lignes doivent être traitées comme des délimiteurs de lignes ou comme une partie d'une chaîne entre guillemets.
Et si vous mettiez le fichier entier dans une variable, puis le divisiez en fonction de nouvelles lignes non citées ?
MODIFIER: Désolé, j'ai mal interprété votre message.Si vous recherchez une regex, en voici une :
content = Regex.Replace(content, "'([^']*)\n([^']*)'", "'\1TOKEN\2'");
Il peut y avoir des cas extrêmes et ces deux problèmes, mais je pense que ça devrait aller la plupart du temps.Ce que fait l'expression régulière, c'est qu'elle trouve d'abord toute paire de guillemets simples qui ont entre eux et remplace ce par TOKEN en préservant tout texte intermédiaire.
Mais quand même, j'irais vers une machine à états comme ce que @bryansh a expliqué ci-dessous.