Подтвердить что ты не робот

Regex для разделения CSV

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

У меня есть файл CSV, и я пытаюсь разбить его на массив, но столкнулся с двумя проблемами: процитированными запятыми и пустыми элементами.

CSV выглядит так:

123,2.99,AMO024,Title,"Description, more info",,123987564

Я пытаюсь использовать регулярное выражение:

thisLine.split(/,(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))/)

Единственная проблема заключается в том, что в моем выходном массиве 5-й элемент выдается как 123987564, а не пустая строка.

4b9b3361

Ответ 1

Описание

Вместо использования раскола, я думаю, было бы проще просто выполнить совпадение и обработать все найденные совпадения.

Это выражение будет:

  • разделите ваш образец текста на разделители запятой
  • будет обрабатывать пустые значения
  • будет игнорировать двойные кавычки, если двойные кавычки не вложены
  • обрезает разделительную запятую из возвращаемого значения
  • обрезает окружающие кавычки из возвращаемого значения

Regex: (?:^|,)(?=[^"]|(")?)"?((?(1)[^"]*|[^,"]*))"?(?=,|$)

enter image description here

Пример

Пример текста

123,2.99,AMO024,Title,"Description, more info",,123987564

Пример ASP с использованием выражения, отличного от java

Set regEx = New RegExp
regEx.Global = True
regEx.IgnoreCase = True
regEx.MultiLine = True
sourcestring = "your source string"
regEx.Pattern = "(?:^|,)(?=[^""]|("")?)""?((?(1)[^""]*|[^,""]*))""?(?=,|$)"
Set Matches = regEx.Execute(sourcestring)
  For z = 0 to Matches.Count-1
    results = results & "Matches(" & z & ") = " & chr(34) & Server.HTMLEncode(Matches(z)) & chr(34) & chr(13)
    For zz = 0 to Matches(z).SubMatches.Count-1
      results = results & "Matches(" & z & ").SubMatches(" & zz & ") = " & chr(34) & Server.HTMLEncode(Matches(z).SubMatches(zz)) & chr(34) & chr(13)
    next
    results=Left(results,Len(results)-1) & chr(13)
  next
Response.Write "<pre>" & results

Совпадает с использованием выражения, отличного от java

Группа 0 получает всю подстроку, которая включает запятую Группа 1 получает цитату, если она использует Группа 2 получает значение, не считая запятой,

[0][0] = 123
[0][1] = 
[0][2] = 123

[1][0] = ,2.99
[1][1] = 
[1][2] = 2.99

[2][0] = ,AMO024
[2][1] = 
[2][2] = AMO024

[3][0] = ,Title
[3][1] = 
[3][2] = Title

[4][0] = ,"Description, more info"
[4][1] = "
[4][2] = Description, more info

[5][0] = ,
[5][1] = 
[5][2] = 

[6][0] = ,123987564
[6][1] = 
[6][2] = 123987564

Ответ 2

Я создал это несколько месяцев назад для проекта.

 ".+?"|[^"]+?(?=,)|(?<=,)[^"]+

Regular expression visualization

Он работает в С#, и Debuggex был счастлив, когда я выбрал Python и PCRE. Javascript не распознает эту форму: ? < =....

Для ваших значений он будет создавать совпадения на

123
,2.99
,AMO024
,Title
"Description, more info"
,
,123987564

Обратите внимание, что что-либо в кавычках не имеет ведущей запятой, но попытка для использования с ведущей запятой была обязательной для пустого варианта использования. После того, как это сделано, отрегулируйте значения по мере необходимости.

Я использую RegexHero.Net для проверки моего регулярного выражения.

Ответ 3

Мне тоже нужен был этот ответ, но я нашел ответы, хотя и информативные, немного трудно следовать и тиражировать для других языков. Вот простейшее выражение, которое я придумал для одного столбца из строки CSV. Я не раскалываюсь. Я создаю регулярное выражение для соответствия столбцу из CSV, поэтому я не разделяю строку:

("([^"]*)"|[^,]*)(,|$)

Это соответствует одному столбцу из строки CSV. Первая часть "([^"]*)" выражения должна совпадать с цитируемой записью, вторая часть [^,]* должна совпадать с некомандной записью. Затем либо следует , либо конец строки $,

И сопроводительный debuggex для проверки выражения.

https://www.debuggex.com/r/s4z_Qi2gZiyzpAhx

Ответ 4

Я опаздываю на вечеринку, но следующее регулярное выражение, которое я использую:

(?:,"|^")(""|[\w\W]*?)(?=",|"$)|(?:,(?!")|^(?!"))([^,]*?)(?=$|,)|(\r\n|\n)

Этот шаблон имеет три группы захвата:

  • Содержимое указанной ячейки
  • Содержимое некотируемой ячейки
  • Новая строка

Этот шаблон обрабатывает все из следующих действий:

  • Нормальное содержимое ячейки без каких-либо специальных функций: один, 2, три
  • Ячейка, содержащая двойную кавычку ( "сбегает к" "): нет цитаты," a "quoted" "thing", end
  • Ячейка содержит символ новой строки: один, два \n три, четыре
  • Нормальное содержимое ячейки, которое имеет внутреннюю котировку: один, два "три, четыре
  • Ячейка содержит кавычку с запятой: один, "два", три ", четыре", пять

См. этот шаблон в использовании.

Если вы используете более способный аромат регулярного выражения с названными группами и lookbehinds, я предпочитаю следующее:

(?<quoted>(?<=,"|^")(?:""|[\w\W]*?)*(?=",|"$))|(?<normal>(?<=,(?!")|^(?!"))[^,]*?(?=(?<!")$|(?<!"),))|(?<eol>\r\n|\n)

См. этот шаблон в использовании.

Ответ 5

Преимущество использования JScript для классических страниц ASP заключается в том, что вы можете использовать одну из многих, многих библиотек, написанных для JavaScript.

Как этот: https://github.com/gkindel/CSV-JS. Загрузите его, включите его на странице ASP, проанализируйте CSV с ним.

<%@ language="javascript" %>

<script language="javascript" runat="server" src="scripts/csv.js"></script>
<script language="javascript" runat="server">

var text = '123,2.99,AMO024,Title,"Description, more info",,123987564',
    rows = CSV.parse(line);

    Response.Write(rows[0][4]);
</script>

Ответ 6

Я лично пробовал много выражений RegEx, не найдя идеального, соответствующего всем случаям.

Я думаю, что регулярные выражения трудно настроить правильно, чтобы правильно соответствовать всем случаям. Хотя немногим людям не понравится пространство имен (и я был их частью), я предлагаю что-то, что является частью структуры .Net и всегда даю мне правильные результаты во всех случаях (в основном, управление всеми случаями двойных кавычек):

Microsoft.VisualBasic.FileIO.TextFieldParser

Нашел его здесь: fooobar.com/questions/209890/...

Пример использования:

TextReader textReader = new StringReader(simBaseCaseScenario.GetSimStudy().Study.FilesToDeleteWhenComplete);
Microsoft.VisualBasic.FileIO.TextFieldParser textFieldParser = new TextFieldParser(textReader);
textFieldParser.SetDelimiters(new string[] { ";" });
string[] fields = textFieldParser.ReadFields();
foreach (string path in fields)
{
    ...

Надеюсь, что это поможет.

Ответ 7

Поработал над этим немного и придумал это решение:

(?:,|\n|^)("(?:(?:"")*[^"]*)*"|[^",\n]*|(?:\n|$))

Попробуйте здесь!

Это решение обрабатывает "хорошие" данные CSV, такие как

"a","b",c,"d",e,f,,"g"

0: "a"
1: "b"
2: c
3: "d"
4: e
5: f
6:
7: "g"

и более уродливые вещи, такие как

"""test"" one",test' two,"""test"" 'three'","""test 'four'"""

0: """test"" one"
1: test' two
2: """test"" 'three'"
3: """test 'four'"""

Здесь объясняется, как это работает:

(?:,|\n|^)      # all values must start at the beginning of the file,  
                #   the end of the previous line, or at a comma  
(               # single capture group for ease of use; CSV can be either...  
  "             # ...(A) a double quoted string, beginning with a double quote (")  
    (?:         #        character, containing any number (0+) of  
      (?:"")*   #          escaped double quotes (""), or  
      [^"]*     #          non-double quote characters  
    )*          #        in any order and any number of times  
  "             #        and ending with a double quote character  

  |             # ...or (B) a non-quoted value  

  [^",\n]*      # containing any number of characters which are not  
                # double quotes ("), commas (,), or newlines (\n)  

  |             # ...or (C) a single newline or end-of-file character,  
                #           used to capture empty values at the end of  
  (?:\n|$)      #           the file or at the ends of lines  
)

Ответ 8

В Java этот шаблон ",(?=([^\"]*\"[^\"]*\")*(?![^\"]*\"))" почти работает для меня:

String text = "\",\",\",,\",,\",asdasd a,sd s,ds ds,dasda,sds,ds,\"";
String regex = ",(?=([^\"]*\"[^\"]*\")*(?![^\"]*\"))";
Pattern p = Pattern.compile(regex);
String[] split = p.split(text);
for(String s:split) {
    System.out.println(s);
}

выход:

","
",a,,"

",asdasd a,sd s,ds ds,dasda,sds,ds,"

Недостаток: не работает, когда столбец имеет нечетное количество кавычек: (

Ответ 9

Еще один ответ с несколькими дополнительными функциями, такими как поддержка цитируемых значений, содержащих экранированные кавычки и символы CR/LF (одиночные значения, которые охватывают несколько строк).

ПРИМЕЧАНИЕ.. Хотя нижеприведенное решение может быть адаптировано для других двигателей регулярных выражений, использование его как есть потребует, чтобы ваш механизм regex рассматривал несколько названных групп захвата с тем же именем, что и одна группа захвата. (.NET делает это по умолчанию)


Когда несколько строк/записей файла/потока CSV (сопоставление RFC standard 4180) передаются в регулярное выражение ниже, оно возвращается совпадение для каждой непустой строки/записи. Каждое совпадение будет содержать группу захвата с именем Value, которая содержит зафиксированные значения в этой строке/записи (и потенциально группу захвата OpenValue, если в конце строки/записи была открытая цитата).

Здесь прокомментированный шаблон (проверьте его на Regexstorm.net):

(?<=\r|\n|^)(?!\r|\n|$)                       // Records start at the beginning of line (line must not be empty)
(?:                                           // Group for each value and a following comma or end of line (EOL) - required for quantifier (+?)
  (?:                                         // Group for matching one of the value formats before a comma or EOL
    "(?<Value>(?:[^"]|"")*)"|                 // Quoted value -or-
    (?<Value>(?!")[^,\r\n]+)|                 // Unquoted value -or-
    "(?<OpenValue>(?:[^"]|"")*)(?=\r|\n|$)|   // Open ended quoted value -or-
    (?<Value>)                                // Empty value before comma (before EOL is excluded by "+?" quantifier later)
  )
  (?:,|(?=\r|\n|$))                           // The value format matched must be followed by a comma or EOL
)+?                                           // Quantifier to match one or more values (non-greedy/as few as possible to prevent infinite empty values)
(?:(?<=,)(?<Value>))?                         // If the group of values above ended in a comma then add an empty value to the group of matched values
(?:\r\n|\r|\n|$)                              // Records end at EOL


Здесь необработанный шаблон без всех комментариев или пробелов.

(?<=\r|\n|^)(?!\r|\n|$)(?:(?:"(?<Value>(?:[^"]|"")*)"|(?<Value>(?!")[^,\r\n]+)|"(?<OpenValue>(?:[^"]|"")*)(?=\r|\n|$)|(?<Value>))(?:,|(?=\r|\n|$)))+?(?:(?<=,)(?<Value>))?(?:\r\n|\r|\n|$)


Вот визуализация от Debuggex.com (группы захвата названы для ясности): Debuggex.com visualization

Примеры использования шаблона регулярных выражений можно найти в моем ответе на аналогичный вопрос здесь или на здесь С# здесь, или здесь.

Ответ 10

Я использую этот, он работает с разделителем комы и сбрасыванием двойной кавычки. Обычно это должно решить вашу проблему:

/(?<=^|,)(\"(?:[^"]+|"")*\"|[^,]*)(?:$|,)/g

Ответ 11

Ааа и еще один ответ. :) Так как я не мог заставить других работать.

Мое решение позволяет обрабатывать скрытые кавычки (двойные вхождения) и не включает разделители в матче.

Обратите внимание, что я сопоставлялся с ' вместо " поскольку это был мой сценарий, но просто заменил их в шаблоне для того же эффекта.

Здесь (не забудьте использовать флаг "игнорировать пробелы" /x если вы используете прокомментированную версию ниже):

# Only include if previous char was start of string or delimiter
(?<=^|,)
(?:
  # 1st option: empty quoted string (,'',)
  '{2}
  |
  # 2nd option: nothing (,,)
  (?:)
  |
  # 3rd option: all but quoted strings (,123,)
  # (included linebreaks to allow multiline matching)
  [^,'\r\n]+
  |
  # 4th option: quoted strings (,'123''321',)
  # start pling
  ' 
    (?:
      # double quote
      '{2}
      |
      # or anything but quotes
      [^']+
    # at least one occurance - greedy
    )+
  # end pling
  '
)
# Only include if next char is delimiter or end of string
(?=,|$)

Однострочная версия:

(?<=^|,)(?:'{2}|(?:)|[^,'\r\n]+|'(?:'{2}|[^']+)+')(?=,|$)

Regular expression visualization (if it works, debux has issues right now it seems - else follow the next link)

Демоверсия Debuggex

пример regex101

Ответ 12

У меня была аналогичная потребность в разделении значений CSV из операторов SQL insert.

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

csv.split(/,((?=')|(?=\d))/g).filter(function(x) { return x !== '';});

По какой-то, вероятно, очевидной причине, это регулярное выражение создает несколько пустых результатов. Я мог игнорировать их, поскольку любые пустые значения в моих данных были представлены как ...,'',..., а не ...,,....

Ответ 13

Если я попробую регулярное выражение, отправленное @chubbsondubs на http://regex101.com, используя флаг 'g', есть совпадения, содержащие только ', 'или пустую строку. С этим регулярным выражением:
(?:"([^"]*)"|([^,]*))(?:[,])
Я могу совместить части CSV (вставляя цитируемые части). (Строка должна быть прервана символом ",", иначе последняя часть не будет распознана.)
https://regex101.com/r/dF9kQ8/4
Если CSV выглядит так:
"",huhu,"hel lo",world,
4 матча:
''
'huhu'
'hel lo'
"мир"

Ответ 14

Если вы знаете, что у вас не будет пустого поля (,), то это выражение хорошо работает:

("[^"]*"|[^,]+)

Как в следующем примере...

Set rx = new RegExp
rx.Pattern = "(""[^""]*""|[^,]+)"
rx.Global = True
Set col = rx.Execute(sText)
For n = 0 to col.Count - 1
    if n > 0 Then s = s & vbCrLf
    s = s & col(n)
Next

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

...
Set col = rx.Execute(Replace(sText, ",,", ", ,"))
...

И если вам нужно поддерживать целостность полей, вы можете восстановить запятые и протестировать пустые пространства внутри цикла. Это может быть не самый эффективный метод, но он выполняет свою работу.

Ответ 15

,?\s*'.+?'|,?\s*".+?"|[^"']+?(?=,)|[^"']+  

Это регулярное выражение работает с одинарными и двойными кавычками, а также для одной цитаты внутри другой.

Ответ 16

Это соответствует всем, что мне нужно в С#:

(?<=(^|,)(?<quote>"?))([^"]|(""))*?(?=\<quote>(?=,|$))
  • листы кавычек
  • позволяет создавать новые строки
  • позволяет использовать двойные кавычки в цитируемой строке
  • позволяет запятые в цитируемой строке

Ответ 17

Правильное регулярное выражение для соответствия одному кавычкому значению со скрытыми [двойными] одинарными кавычками в нем:

'([^n']|(''))+'