Всем доброго дня!
Есть у меня задачка — загрузить в некую БД сведения из файлов формата XLS. Загрузка осуществляется программой, которая извлекает данные из файла XLS, при помощи COM-объекта Excel. Как следствие, данные выгружаются крайне долго и печально, а учитывая что файлов около десятка и в каждом по 7000-10000 строк, то всё весьма не быстро.
Поэтому было решено использовать Excel для превращения файла XLS в CSV, а потом обработка CSV как массива строк. Строки предполагалось разбирать при помощи регулярного выражения, используя встроенный в Windows процессор регулярных выражений
VBScript.RegExp.
По умолчанию (и в соответствии с настройками системы) в качестве разделителя Excel использует точку с запятой. Всё было бы хорошо, пока в файлах не стали попадаться значения такого вида:
123;456;7";"89
Очевидно, что Excel экранирует символ-разделитель, обрамляя его кавычками. И это бы ничего, но в файлах встречаются значения и такого вида:
123;456;"122";"789; ... то есть полная мешанина из кавычек и символов-разделителей.
Проблема состоит в том, как быстро и просто разобрать такой файл по отдельным значениям. Excel с такой задачей справляется великолепно.
У меня родилось несколько идей, как это сделать:
Нормализовать файл;
Использовать регулярное выражение.
Под нормализацией файла я понимаю замену всех встреченных в файле символов-разделителей (;) на какой-нибудь нейтральный символ, который потом легко заменит назад, например на символ "неразрывный пробел" (CHAR(160)).
После того, как файл будет нормализован, к каждой строке файла применяется регулярное выражение, которое извлекает хранимое значение. Полученное значение надо проверить на наличие символа "неразрывного пробела" и если он там есть, то заменить его на символ-разделитель.
Вариант плох тем, что надо делать предварительную обработку данных, потом использовать регулярное выражение, а потом производить обратную замену данных. Ввиду этих особенностей родился второй вариант — сразу написать регулярное выражение, разбирающее строку нужным образом. При реализации данного способа я столкнулся с той проблемой, что моих навыков создания регулярных выражений недостаточно для написания РВ, сразу корректно разбирающего строку, с учётом кавычек, наличия символов-разделителей в строке и т.п.
Я покопался в интернете и нашёл пару РВ, максимально близко решающих мою задачу:
,(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))
(""([^""]*|""{2})*""(,|$))|""[^""]*""(,|$)|[^,]+(,|$)|(,)
Ввиду того, что парсер заменяет скобку с точкой с запятой на смайлик, точка с запятой заменена просто запятой.
Первое регулярное выражение находит в строке все символы-разделители, отделяя те, которые экранированы кавычками:
123;456;7890 -> (;)(3)(;)(7), где (3)(7) — позиции символа-разделителя.
123;456;78";"90 -> (;)(3)(;)(7)
Строки, полученные
Второе выражение выделяет сразу элементы строки, но всё равно требуется пост-обработка — удаление символа-разделителя с концов извлеченных групп символов, и удаление парных кавычек по краям извлечённого текста. И да, второе выражение работает некорректно, например, на такой строке: 123;"456;""""";789
Выражение считает, что в строке 4 элемента, хотя данная строка была получена из 3 ячеек: 123 | 456;"" | 789
Собственно, прошу помощи в следующем:
Решал ли кто-нибудь аналогичную задачу и если да, то может быть знает более элегантный способ её решения?
Помогите оптимизировать какое-либо из существующих регулярных выражений, или помогите написать новое РВ, которое бы корректно отрабатывало на файлах CSV, продуцируемых Microsoft Excel 2010
Спасибо!