Как можно проанализировать этот файл в C #, где у меня есть CRLF внутри поля - PullRequest
1 голос
/ 12 июля 2010

Я пытаюсь разобрать файл, который выглядит так:

||Заголовок столбца A ||Заголовок столбца B ||Заголовок столбца C || CRLF |Данные А |Данные Б |Данные C | CRLF |Данные А |Данные Б |Данные C | CRLF

CRLF » обозначает разрыв строки)

У меня был код для разбора этого штрафа:

Сначала я анализирую файл в массив строк:

 string[] lines = fileString.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);

Затем я анализирую каждую строку в массиве значений данных столбцов,

Сначала анализирую, чтобы получитьиспользуя заголовок:

  string Delimiter = "||";
  string[] columns = line.Split(new string[] { Delimiter }, StringSplitOptions.RemoveEmptyEntries);

Затем выполните синтаксический анализ остальных строк, используя

    string Delimiter = "|";
  string[] columns = line.Split(new string[] { Delimiter }, StringSplitOptions.RemoveEmptyEntries);

. Это работало идеально, пока я не нашел запись, которая содержала CRLF внутриполе, таким образом, мой анализ разбился.

Может кто-нибудь придумать хороший способ разбора этих данных ниже и правильно обрабатывать CRLF ?Вот пример:

||Заголовок столбца A ||Заголовок столбца B ||Заголовок столбца C || CRLF |Данные А |Данные Б |Данные C | CRLF |Данные А |Данные B CRLF Продолжение B |Данные C | CRLF

Проблема в том, что когда я делаю начальный анализ для получения массива строк, я получаю здесь 4 строки вместо 3 (потому что последняя строка отображается как две записи в этом массиве.)

Ответы [ 6 ]

3 голосов
/ 12 июля 2010

Здесь у вас есть текст с разделителями.String.Split () - заведомо наивный выбор для анализа данных такого типа.Это медленно и склонно к проблемам, таким как то, что вы испытываете сейчас.Лучшее решение - это что-то вроде класса Microsoft.VisualBasic.TextFieldParser или Fast CSV-парсера для проекта кода .

2 голосов
/ 12 июля 2010

Не совсем элегантно, но это решение грубой силы приходит на ум первым. Разделите, а затем объедините, если коротко:

var lines = content.Split(...);
string header[] = lines[0].Split(...);
int numberOfColumns = header.Length;

var parsedLines = new List<string[]>();
for (int i = 1; i < lines.Length; i++) {
   var line = lines[i];

   while ((fields = line.Split(...)).Length < numberOfColumns) {
     // combine with next, and increment i
     line += lines[++i];
   }

   parsedLines.Add(fields);
}
1 голос
/ 12 июля 2010

В этом случае есть простое исправление:

Возьмите одну строку.Это заканчивается на |?Если нет, добавьте CRLF и следующую строку к нему.Повторяйте до тех пор, пока он не закончится на |, а затем проанализируйте его.

0 голосов
/ 12 июля 2010

Это классический пример неверных данных, или, скорее, неправильный выбор разделителей.Перед написанием парсера вы должны быть на 100% уверены в данных, ожидаемых вашим кодом.

В этом случае вы столкнулись с CRLF в ваших данных, как бы вы (или ваш код) узнали, что на самом деле это не разделитель?

Я бы сказал, используйте лучший разделитель, если у вас естьвыбор.

РЕДАКТИРОВАТЬ: Вам необходимо иметь представление с отправителем о разделителе, и тогда ответственность за обеспечение качества данных лежит на отправителе.

Глядя на ваши примеры данных, «| CRLF» представляется хорошим разделителем вместо «CRLF».Но как вы (синтаксический анализатор) убедитесь, что этот разделитель не встречается в реальных данных?Ты не можешь.Что вы можете сделать, это проверить качество данных по шаблону, согласованному с отправителем (например, количество столбцов в записи и т. Д.).А если проверка не пройдена, сообщите об ошибке отправителю и запросите повторную передачу.

Лучше всего было бы, чтобы отправитель дал вам заголовок с деталями данных (т.е. без записей,нет столбцов и т. д.)

Как парсер, ваш контроль над данными ограничен.Эта проблема нуждается в поддержке со стороны отправителя.

0 голосов
/ 12 июля 2010

Просто и идея основана на том, что вы показали в вопросе:

Удалите все CRLF , которые не появляются сразу после | или || оставляя последний там (чтобы обозначить разрыв строки). Делая это, я думаю, что ваш текущий код будет работать так, как вы хотите.

Примерно так:

string wrongLine = "| Data A | Data B \r\n Continued B | Data C |\r\n";

string rightLine = wrongLine.Replace(" " + Environment.NewLine, string.Empty);

Это даст вам вывод (с сохранением последнего CRLF):

"| Data A | Data B Continued B | Data C |\r\n"
0 голосов
/ 12 июля 2010

Вы должны рассмотреть библиотеку разбора CSV.

Тем не менее, вы можете сделать что-то , например (больше подтверждение концепции, чем лучший случай), если вы действительно против этого пути и можете гарантировать, что в заголовках ваших столбцов нет разных CRLF

string Delimiter = "||"; 

string[] columns = fileString.Substring(0, fileString.IndexOf(Environment.NewLine))
   .Split(new string[] { Delimiter }, StringSplitOptions.RemoveEmptyEntries); 

string[] cells = fileString.Substring(fileString.IndexOf(Environment.NewLine))
   .Split(new string[] { Delimiter }, StringSplitOptions.RemoveEmptyEntries); 

List<string> rows = new List<string>();
StringBuilder row = new StringBuilder();
int colIndex = 0;
int breakIndex = columns.Length;
char[] trimChars = new char[] { '\r','\n',' ' };

foreach(string c in cells)
{
   if (cellIndex == breakIndex)
   {
       rows.Add(row.ToString().Trim(trimChars));
       cellIndex = 0;
       row = new StringBuilder();
   }
   row.Append(c).Append(" ");
   cellIndex ++;
}
rows.Add(row.ToString().Trim(trimChars));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...