Задать вопрос
@vhelsing90
Студент технического вуза

Как парсить текст в CSV формате игнорируя запятые внутри кавычек и без сторонних библиотек?

Например есть строка: имя, дни, компания.
Разделить строку легко с помощью метода Split:
string[] text = File.ReadAllLines("file.csv", Encoding.Default);
            foreach (string line in text)
            {
                string[] words = line.Split(',');
                foreach (string word in words)
                {
                    Console.WriteLine(word);
                }
            }
            Console.ReadKey();


Но как парсить, если все, что обрамляется двойными кавычками, является текстом, даже при наличии внутри запятых. Например:
"Сергей, Корольков", 7 дней, Ариель
Максим, 3 дня, "компания, Орифлейм"

Должно вывести:

Сергей, Корольков | 7 дней | Ариель
Максим | 3 дня | компания, Орифлейм

Но нужно учитывать, что входные данные не всегда будут в идеальном формате (как в примере). То есть могут встретиться 3 кавычки подряд или строка без запятых. Программа не должна падать ни при каком варианте. Хотя Если распарсить невозможно, я буду выдавать сообщение об этом.
  • Вопрос задан
  • 1229 просмотров
Подписаться 1 Простой 3 комментария
Решения вопроса 1
@kttotto
пофиг на чем писать
Можно примерно так
data = new List<string>();
parts = source.Split("\"");
data.AddRange(parts.Where((x, index) => index % 2 != 0));
data.AddRange(parts.Where((x, index) => index % 2 == 0).Split(","));
result = string.Join(" | ", data.Where(x => !string.IsNullOrWhiteSpace(x));

Но если будет три кавычки, то это может не сработать.
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
@Sumor
По мотивам ответа mefutu
Примерное решение на конечном автомате:
using System;
using System.Text;
using System.Collections.Generic;
					
public class Program
{
	public static void Main()
	{
		Console.WriteLine(string.Join("|", Parse("Мама,\"мыла, блин\", раму,\"мама, мыла \"\"раму\"\"\",конец")));
	}
	
	public enum StateEnum{Start, StartQuot, Inline, InlineQuot}
	
	public static IEnumerable<string> Parse(string str)
	{
		var state = StateEnum.Start;
		var sb = new StringBuilder();
		foreach(var ch in str)
		{
			switch(ch)
			{
				case '"':
					switch(state)
					{
						case StateEnum.Start:
							state = StateEnum.StartQuot;
							continue;
						case StateEnum.StartQuot:
						case StateEnum.InlineQuot:
							state = StateEnum.Inline;
							sb.Append('"');
							continue;
						case StateEnum.Inline:
							state = StateEnum.InlineQuot;
							continue;
					}
					break;
				case ',':
					switch(state)
					{
						case StateEnum.Start:
						case StateEnum.InlineQuot:
							yield return sb.ToString();
							sb.Clear();				
							state = StateEnum.Start;
							continue;
						case StateEnum.StartQuot:
						case StateEnum.Inline:
							sb.Append(',');
							state = StateEnum.Inline;
							continue;						
					}
					goto default;
				default:
					sb.Append(ch);
					break;
			}
		}
		yield return sb.ToString();
	}
}
Ответ написан
Комментировать
@mefutu
Ну здесь вам split не поможет. Возьмите/сделайте "stateMachine" простой пример тут : https://stackoverflow.com/questions/5923767/simple... . Проходите по каждому символу. Смотрите на состояние машины и принимаете решение включать ли этот символ к вам в строку либо пора строку уже пушить в память.

Примерный алгоритм:
Открылась кавычка;
- сохраняем все в строку
Закрылась кавычка;
- ждем символ разделитель ',' ';'

P.s statemachine здесь может быть избыточна, но для справки почитайте.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы