Что нового

[RegExp] Деление строки по разделителю с условием что разделитель не найден в кавычках

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
Пытаюсь получить в массив разделённую строку по определённому делителю (запятая), но его нужно проигнорировать если он находится в кавычках.

Код:
$vTest = 'Item1, "Folk, NewAge и Flamenco (DVD Video)", Item2,'

$sRet = StringRegExp($vTest, '([^,]+),?\s*', 3)

For $i = 0 To UBound($sRet)-1
	ConsoleWrite($sRet[$i] & @LF)
Next


Ожидаемый результат:

Код:
Item1
"Folk, NewAge и Flamenco (DVD Video)"
Item2
 

Suppir

Продвинутый
Сообщения
967
Репутация
62
Если по-быстрому, то так можно:

Код:
$vTest = 'Item1, "Folk, NewAge и Flamenco (DVD Video)", Item2,'

$sModified = StringRegExpReplace($vTest, " ""(.+?),(.+?)""", """\1~\2""")

$sRet = StringRegExp($sModified, '([^,]+),?\s*', 3)

For $i = 0 To UBound($sRet)-1
	$sRet[$i] = StringRegExpReplace($sRet[$i], "~", ",")
    ConsoleWrite($sRet[$i] & @LF)
Next



Суть:
1) первым проходом меняем запятую, находящуюся внутри кавычек, на уникальный знак;
2) спокойно делаем разделение по запятым;
3) обратно меняем уникальный знак на запятую.
 
Автор
CreatoR

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
Suppir [?]
Если по-быстрому, то так можно
Это не просто по быстрому, это предусматривает лишь приведённую выше строку, а ведь запятых может быть несколько, и кавычек вообще может и не быть, и к тому же, придётся убедиться что уникального символа в строке нет, а это ещё один проход замены...
 

Suppir

Продвинутый
Сообщения
967
Репутация
62
Насчет уникального символа - можно просто редкую комбинацию написать и не проверять ничего.

Насчет двух и более запятых нужно подумать.
 
Автор
CreatoR

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
Suppir [?]
Насчет двух и более запятых нужно подумать.
Вот что я надумал:

Код:
$vTest = 'Item1, "Folk, NewAge и Flamenco, (DVD Video)", Item2,'

$sStr = StringRegExpReplace($vTest, '(?m).*(("|'')[^\2]*?\2).*', '\1')
$vTest = StringRegExpReplace($vTest, '(?m)(.*)(("|'')[^\3]*?\3)(.*)', '\1~@_#_#_@~\4')
$sRet = StringRegExp($vTest, '([^,]+),?\s*', 3)

For $i = 0 To UBound($sRet)-1
    $sRet[$i] = StringRegExpReplace($sRet[$i], "~@_#_#_@~", $sStr)
    ConsoleWrite($sRet[$i] & @LF)
Next


:laugh:
 

amel27

Продвинутый
Сообщения
146
Репутация
55
если попытаться в одну строчку:

Код:
StringRegExp($s,'(?:[^,]++|,(?!([^"'']*+(?:"[^"]*+"|''[^'']*+'')?)*$))+', 3)
 

Suppir

Продвинутый
Сообщения
967
Репутация
62
А у меня вот такое решение 8)

Код:
StringRegExp($s, '(?:[^",]+|"[^"]*")++', 3)
 

amel27

Продвинутый
Сообщения
146
Репутация
55
Suppir
класс! осталось только добавить поддержку одинарных кавычек:

Код:
StringRegExp($sText, '(?:[^"'',]++|"[^"]*+"|''[^'']*+'')++', 3)
 
Автор
CreatoR

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
amel27 [?]
класс! осталось только добавить поддержку одинарных кавычек
Этот RegExp не уступает твоему по надёжности? :whistle:
И одинарные кавычки не нужны, это пункты в меню браузера Opera, там только двойные поддерживаются (обрамляют параметры указанных действий).
 

amel27

Продвинутый
Сообщения
146
Репутация
55
CreatoR сказал(а):
Этот RegExp не уступает твоему по надёжности?
на самом деле они очень похожи, чуточек недодумал... :smile:
вариант Suppir проще, а значит надежней :thumbs_up:



Добавлено:
Сообщение автоматически объединено:

разве только немного оптимизировал бы (не знаю почему Suppir это не сделал):

Код:
StringRegExp($s, '(?:[^",]++|"[^"]*+")++', 3)
 

glax24

Знающий
Сообщения
72
Репутация
14
С помощью представленной здесь регулярки получается следующий результат
Код:
$vTest = ',строка1,,,"строка2","строка3,строка,",,,,,,,'

$sRet = StringRegExp($vTest, '(?:[^",]++|"[^"]*+")++', 3)
For $i = 0 To UBound($sRet) - 1
	ConsoleWrite("[" & $i & "] " & $sRet[$i] & @LF)
Next

Результат
Код:
[0] строка1
[1] "строка2"
[2] "строка3,строка,"
Как её доработать чтобы возвращала следующий результат
Код:
[0] 
[1] строка1
[2] 
[3] 
[4] "строка2"
[5] "строка3,строка,"
[6] 
[7] 
[8] 
[9] 
[10] 
[11]
 

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
У меня вот так получилось.

Код:
#Include <Array.au3>
$vTest = ',строка1,,,"строка2","строка3,строка,",,,,,,,'
$sPattern = '(?<=^|,|")\s*(?(?=")"[^"]*"|[^,]*)(?!\z)'
$aResult = StringRegExp($vTest, $sPattern, 3)
_ArrayDisplay($aResult)


Правда элементов у меня на один больше - я считаю что после последней запятой находится ещё один элемент.
:smile:


Добавлено:
Сообщение автоматически объединено:

Цитата Redline: важное замечание:
Есть один неприятный момент - при использовании условных масок (даже при использовании групп без захвата) в вывод попадает все что лежит за условной подмаской и записано в шаблоне, во всяком случае у меня не получилось иначе.
Т.е. в вывод второго примера попадают пробелы. Если в первом примере часть шаблон изменить на такой: "(?:blum:ана:\s)(?(?=Р).+|[^;]+)" или "рана:\s(?(?=Р).+|[^;]+)", то результат будет одинаков (кусок слова "рана" из "Страна" попадет в вывод).
Если кто-то найдет решение - прошу написать об этом, но ИМХО тут дело не в условных масках, а в самом механизме RegExp.


Вряд ли это можно назвать полноценным решением...
В шаблоне с условной маской вместо группы без захвата я использовал "Флаги групп"
http://autoit-script.ru/autoit3_docs/functions/StringRegExp.htm
Они, как оказалось, в описаной проблемной ситуации в вывод не попадают.
:smile:
 

glax24

Знающий
Сообщения
72
Репутация
14
Это выражение не подходит тк есть такие строки
Код:
$sStr = ',строка1,,,"строка2,строка","строка3,строка,",,,,,,"""cтрока4"" cтрока5"'
а представленное выше разбирает такие строки нормально, но надо еще пустые строки добавить
 

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
Подправил. Теперь и такие строки разбирает.
;D
 

glax24

Знающий
Сообщения
72
Репутация
14
Не правильно разбирает тк разделителя , между ними нет
Код:
[13] ""
[14] "cтрока4"
[15] " cтрока5"
[16]
а надо
Код:
[13] """cтрока4"" cтрока5"
 

MnM

Post-Hardcore
Сообщения
679
Репутация
90
glax24
А вам в массив обязательно смешанный тип строк? например в 1 ячейке у вас просто текст, а в 4 и др. он в кавычках.
 

firex

AutoIT Гуру
Сообщения
943
Репутация
208
На мой взгляд такое распарсить невозможно. Хотел было сделать вот так:

Код:
Local $_sText = ClipGet()
Local $_aSplit = StringSplit( $_sText, ',' )
Local $_aResult[666] = [1], $_sTemp = '', $_bCheck

For $Idx = 1 To $_aSplit[0] Step 1
    $_bCheck = __Check( $_aSplit[$Idx], '"' )
	MsgBox( 64, '', $_bCheck )

    If $_bCheck And $_sTemp = '' Then
        $_aResult[$_aResult[0]] = $_aSplit[$Idx]
        $_aResult[0] += 1
    ElseIf Not $_bCheck And $_sTemp <> '' Then
        $_aResult[$_aResult[0]] = $_sTemp
        $_aResult[0] += 1
        $_sTemp = ''
    Else
        $_sTemp &= $_aSplit[$Idx]
    EndIf
Next

_ArrayDisplay( $_aResult )

Func __Check( $Str, $s )
    Local $Array = StringRegExpReplace( $Str, '(' & $s & ')', '' )

    Return (Mod( @Extended, 2 ) = 0)
EndFunc


Но в ситуации присутствия запятой между "" - она пропадет.

Поэтому сделал так:
Код:
#Include <Array.au3>

;,строка1,,,"строка2,строка","строка3,строка,",,,,,,"""cтрока4"" cтрока5"

Local $_sText = ClipGet()
Local $_iLen = StringLen( $_sText ) + 1
Local $_aResult[666] = [1], $_sCur, $_iOpens = 0, $_sTemp = ''
For $Idx = 1 To $_iLen Step 1
	$_sCur = StringMid( $_sText, $Idx, 1 )

	If ($_sCur = ',' Or $_iLen = $Idx) And Mod( $_iOpens, 2 ) = 0 Then
		$_iOpens = 0
		$_aResult[$_aResult[0]] = $_sTemp
		$_aResult[0] += 1
		$_sTemp = ''
		ContinueLoop
	ElseIf $_sCur = '"' Then
		$_iOpens += 1
	EndIf

	$_sTemp &= $_sCur
Next

_ArrayDisplay( $_aResult )
 

sngr

AutoIT Гуру
Сообщения
1,010
Репутация
408
Код:
#include 'array.au3'
;~ $vTest = ',строка1,,,"строка2","строка3,строка,",,,,,,,'
$vTest  = ',строка1,,,"строка2,строка","строка3,строка,",,,,,,"""cтрока4"" cтрока5"'
$sRet = StringRegExp($vTest, '(?<=^|,)(?:"[^"]*+"|[^,"])*+', 3)
_ArrayDisplay($sRet)
 

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
Ну, я, естественно, дописываю свой шаблон в связи с появившимися новыми требованиями.
Код:
#Include <Array.au3>
$vTest = 'Item1, "Folk, NewAge и Flamenco (DVD Video)", Item2,'
$vTest &= ',строка1,,,"строка2","строка3, строка,",,,,,,,'
$vTest &= ',строка1,,,"строка2,строка","строка3,строка,",,,,,,"""cтрока4"" cтрока5"'
$sPattern = '(?<=^|,)\s*(?(?=").*?"|[^,]*)(?=,|\z)'
$aResult = StringRegExp($vTest, $sPattern, 3)
_ArrayDisplay($aResult)


glax24, старайтесь озвучивать все условия сразу, а не вбрасывать по ходу процесса "а есть ещё вот такие строки...".

sngr, я тут не понимаю... В Вашем шаблоне нет ни одной группы с захватом. Откуда берётся результат?
Вернее не так: почему в результат попадает группа без захвата?
 

glax24

Знающий
Сообщения
72
Репутация
14
sngr
Ваш шаблон то что надо :beer:
C2H5OH
Все новые строки появляются во время разбора, смотрю на каких ошибка те и вбрасываю.
C2H5OH ваш шаблон тоже справился с поставленной задачей. :ok:
Всем спасибо.
 
Верх