Разделимый разделитель CSV в .NET.
У меня есть текстовый файл, который находится в формате, разделенном запятыми, с разделителем "
в большинстве полей. Я пытаюсь получить это во что-то, что я могу перечислить (Generic Collection, например). Я не контролирую, как файл выводится, и символ, который он использует для разделителя.
В этом случае поля разделяются запятой, а текстовые поля заключаются в метки "
. Проблема, с которой я сталкиваюсь, заключается в том, что некоторые поля имеют в них кавычки (т.е. 8 "
Лоток) и случайно попадают в следующее поле. В случае числовых полей у них нет котировок вокруг них, но они начинаются с знака + или а (с изображением положительного/отрицательного числа).
Я думал о RegEx, но мои навыки не настолько велики, поэтому, надеюсь, кто-то может придумать некоторые идеи, которые я могу попробовать. В этом файле содержится около 19 000 записей, поэтому я стараюсь сделать это максимально эффективно. Вот пара примерных строк данных:
"00","000000112260 ","Pie Pumpkin ","RET","6.99 "," ","ea ",+0000000006.99000
"00","000000304078 ","Pie Apple caramel ","RET","9.99 "," ","ea ",+0000000009.99000
"00","StringValue here","8" Tray of Food ","RET","6.99 "," ","ea ",-00000000005.3200
Есть намного больше полей, но вы можете получить изображение....
Я использую VB.NET, и у меня есть общая настройка List для принятия данных. Я попытался использовать CSVReader, и, похоже, он работает хорошо, пока вы не нажмете запись, подобную третьей (с цитатой в текстовом поле). Если бы я мог каким-то образом обработать дополнительные кавычки, то опция CSVReader будет работать отлично.
Спасибо!
Ответы
Ответ 1
Из здесь:
Encoding fileEncoding = GetFileEncoding(csvFile);
// get rid of all doublequotes except those used as field delimiters
string fileContents = File.ReadAllText(csvFile, fileEncoding);
string fixedContents = Regex.Replace(fileContents, @"([^\^,\r\n])""([^$,\r\n])", @"$1$2");
using (CsvReader csv =
new CsvReader(new StringReader(fixedContents), true))
{
// ... parse the CSV
Ответ 2
Я рекомендую посмотреть TextFieldParserClass в .Net. Вы должны включить
Imports Microsoft.VisualBasic.FileIO.TextFieldParser
Вот пример:
Dim afile As FileIO.TextFieldParser = New FileIO.TextFieldParser(FileName)
Dim CurrentRecord As String() ' this array will hold each line of data
afile.TextFieldType = FileIO.FieldType.Delimited
afile.Delimiters = New String() {","}
afile.HasFieldsEnclosedInQuotes = True
' parse the actual file
Do While Not afile.EndOfData
Try
CurrentRecord = afile.ReadFields
Catch ex As FileIO.MalformedLineException
Stop
End Try
Loop
Ответ 3
Попробуйте этот сайт. http://kbcsv.codeplex.com/
Я искал хорошую утилиту, и это лучшая работа, которую я нашел и работает правильно. Не тратьте время на то, чтобы попробовать другие вещи, это бесплатно, и это работает.
Ответ 4
Как говорится в этой ссылке... Не катите свой собственный парсер CSV!
Используйте TextFieldParser, как предлагал Avi. Microsoft уже сделала это за вас. Если вы закончите писать один, и вы найдете в нем ошибку, подумайте о замене его вместо исправления ошибки. Я сделал это недавно, и это спасло меня много времени.
Ответ 5
Посмотрите на Библиотека FileHelpers.
Ответ 6
Вы можете дать CsvHelper (библиотека, которую я поддерживаю), и она доступна через NuGet. Это следует за стандартом RFC 4180 для CSV. Он сможет обрабатывать любой контент внутри поля, включая запятые, кавычки и новые строки.
CsvHelper прост в использовании, но также легко настроить его для работы со многими различными типами файлов с разделителями.
CsvReader csv = new CsvReader( streamToFile );
IEnumerable<MyObject> myObjects = csv.GetRecords<MyObject>();
Если вы хотите читать CSV файлы на более низком уровне, вы можете напрямую использовать парсер, который будет возвращать каждую строку в виде массива строк.
var parser = new CsvParser( myTextReader );
while( true )
{
string[] line = parser.ReadLine();
if( line == null )
{
break;
}
}
Ответ 7
Я отправляю это как ответ, поэтому я могу объяснить, как я это сделал и почему... Ответ от Митча Пшеница был тем, который дал мне лучшее решение для этого случая, и мне просто пришлось немного изменить его в формат, в который эти данные были экспортированы.
Вот код VB:
Dim fixedContents As String = Regex.Replace(
File.ReadAllText(csvFile, fileEncoding),
"(?<!,)("")(?!,)",
AddressOf ReplaceQuotes)
Используемый RegEx - это то, что мне нужно было изменить, потому что у некоторых полей были неэксклюзивные кавычки в них, а предоставленный RegEx не работал во всех примерах. Этот использует "Look Ahead" и "Look Behind", чтобы увидеть, есть ли цитата сразу после запятой или раньше. В этом случае они оба отрицательные (это означает, что я вижу, где двойная кавычка не до или после запятой). Это должно означать, что цитата находится в середине строки.
В этом случае вместо прямой замены я использую функцию ReplaceQuotes для обработки этого для меня. Причина, по которой я использую это, - это то, что мне нужно было немного дополнительной логики, чтобы определить, было ли это в начале строки. Если бы я потратил еще больше времени на это, я уверен, что я мог бы настроить RegEx, чтобы учесть начало строки (используя MultiLine и т.д.), Но когда я попробовал это быстро, он, похоже, не работал все.
С помощью этого метода, используя CSV-ридер в 32-мегабайтном CSV файле (около 19000 строк), требуется около 2 секунд, чтобы прочитать файл, выполнить регулярное выражение, загрузить его в CSV-Reader, добавить все данные в мои общие класса и отделки. Настоящий быстро!
Ответ 8
Существуют, по крайней мере, драйверы ODBC для файлов CSV. Но есть разные вкусы CSV.
Что создавали эти файлы? Не исключено, что существует соответствующий драйвер, основанный на требованиях исходного приложения.
Ответ 9
Ваша проблема с CSVReader заключается в том, что цитата в третьей записи не сбежала с другой цитатой (также называемой двойной кавычкой). Если вы не избежите их, то как вы ожидаете обработать ", в середине текстового поля?
http://en.wikipedia.org/wiki/Comma-separated_values
(Мне пришлось работать с файлами (с разными разделителями), но символы кавычек внутри текстового значения не были экранированы, и я закончил писать собственный собственный парсер. Я не знаю, было ли это абсолютно необходимо или нет.)
Ответ 10
Логика этого пользовательского подхода: Прочитайте файл по 1 строке за раз, разделите каждую строку в запятой, удалите первый и последний символ (удалите внешние кавычки, но не затрагивая какие-либо внутренние кавычки), а затем добавьте данные в ваш общий список. Это короткий и очень легкий для чтения и работы.
Dim fr As StreamReader = Nothing
Dim FileString As String = ""
Dim LineItemsArr() as String
Dim FilePath As String = HttpContext.Current.Request.MapPath("YourFile.csv")
fr = New System.IO.StreamReader(FilePath)
While fr.Peek <> -1
FileString = fr.ReadLine.Trim
If String.IsNullOrEmpty(FileString) Then Continue While 'Empty Line
LineItemsArr = FileString.Split(",")
For Each Item as String In LineItemsArr
'If every item will have a beginning and closing " (quote) then you can just
'cut the first and last characters of the string here.
'i.e. UpdatedItems = Item. remove first and last character
'Then stick the data into your Generic List (Of String()?)
Next
End While
Ответ 11
public static Encoding GetFileEncoding(String fileName)
{
Encoding Result = null;
FileInfo FI = new FileInfo(fileName);
FileStream FS = null;
try
{
FS = FI.OpenRead();
Encoding[] UnicodeEncodings = { Encoding.BigEndianUnicode, Encoding.Unicode, Encoding.UTF8 };
for (int i = 0; Result == null && i < UnicodeEncodings.Length; i++)
{
FS.Position = 0;
byte[] Preamble = UnicodeEncodings[i].GetPreamble();
bool PreamblesAreEqual = true;
for (int j = 0; PreamblesAreEqual && j < Preamble.Length; j++)
{
PreamblesAreEqual = Preamble[j] == FS.ReadByte();
}
if (PreamblesAreEqual)
{
Result = UnicodeEncodings[i];
}
}
}
catch (System.IO.IOException)
{
}
finally
{
if (FS != null)
{
FS.Close();
}
}
if (Result == null)
{
Result = Encoding.Default;
}
return Result;
}
Ответ 12
RegEx для исключения первой и последней цитаты будет (?<!^)(?<!,)("")(?!,)(?!$)
. Конечно, вам нужно использовать RegexOptions.Multiline.
Таким образом, нет необходимости в функции оценщика. Мой код заменяет нежелательные двойные кавычки с одинарными кавычками.
Полный код С# приведен ниже.
string fixedCSV = Regex.Replace(
File.ReadAllText(fileName),
@"(?<!^)(?<!;)("")(?!;)(?!$)", "'", RegexOptions.Multiline);