Что нового

[RegExp] Получение содержания всех разделов по его имени

firex

AutoIT Гуру
Сообщения
943
Репутация
208
Доброго времени суток.

Имеется текст вида:
Код:
SECTION1
{
key1 = value1
key2 = value2
keyN = valueN
SECTION1_1
{
SECTION1_1_1
{
key1 = value1
key2 = value2
keyN = valueN
}
SECTION1_1_2
{
key1 = value1
key2 = value2
keyN = valueN
}
}
}

Имеется регулярное выражение (от CreatoR'а):
Код:
%F%\r?\n{\s+([\S\s]+?)\s+}
Где %F% - имя раздела. Но оно не корректно.

Исходя из этого регулярное выражение должно возвращать по имени раздела содержимое раздела. Как к примеру вот тут(только не останавливаясь на ближайшем '}', а делая полный захват раздела(даже если он включает подразделы):

Код:
$_sText = 'TEST' & @LF & '{' & @LF & 'test1 = test2' & @LF & '}'
$_aResult =  StringRegExp( $_sText , 'TEST\n{\s+([\S\s]+?)\s+}', 3 )
MsgBox( 64, '', $_aResult[0] )


Заранее спасибо.
 
Автор
firex

firex

AutoIT Гуру
Сообщения
943
Репутация
208
gregaz

Теперь всегда на самом последнем символе '}'
Все еще актуально. Без регулярных выражений этот процесс слишком требователен к ресурсам. Файл порядка 1мб загружается около 20 секунд.

Надеюсь на вашу помощь. На всякий случай полноценный пример:
Код:
VESSEL
{
	pid = 05ef4c62f46944c7aff579f5056691c1
	name = FXLM_b1
	type = Ship
	sit = LANDED
	landed = True
	landedAt = 
	splashed = False
	met = 451843695.625706
	lct = 493485.316783616
	root = 0
	lat = 2.42884140463921
	lon = -158.170788381879
	alt = 988.998613539094
	hgt = 5.273009
	nrm = 0.005769582,0.9999543,-0.007633958
	rot = 0.7049874,-0.6646524,0.05567101,-0.2411032
	CoM = -0.0002908502,-2.242053,0.0004771798
	stg = 2
	prst = False
	ref = 4244007397
	cPch = 0
	cHdg = 0
	cMod = 0
	ORBIT
	{
		SMA = NaN
		ECC = 1
		INC = NaN
		LPE = 163.796395124943
		LAN = 0
		MNA = -8.40714670366795
		EPH = 459526557.867619
		REF = 2
		OBJ = 0
	}
	PART
	{
		name = Mark1-2Pod
		uid = 4244007397
		parent = 0
		position = 0,0,0
		rotation = 0,0,0,1
		mirror = 1,1,1
		istg = 0
		dstg = 0
		sqor = -1
		sidx = -1
		attm = 0
		srfN = None, -1
		attN = bottom, 2
		attN = top, 1
		mass = 4
		temp = 0
		expt = 0.5
		state = 0
		connected = True
		attached = True
		flag = Squad/Flags/hexagon
		rTrf = Mark1-2Pod (FXLM_b1)
		crew = 0
		crew = 3
		EVENTS
		{
		}
		ACTIONS
		{
		}
		MODULE
		{
			name = ModuleCommand
			isEnabled = True
			controlSrcStatusText = Operational
			EVENTS
			{
			}
			ACTIONS
			{
			}
		}
		RESOURCE
		{
			name = ElectricCharge
			amount = 150
			maxAmount = 150
			flowState = True
			flowMode = Both
		}
	}
}
*TAB'ы удаляются изначально, а расставляются лишь при сохранении. Их не учитывайте.

Если к примеру мы запрашиваем EVENTS, то должно вернуться:
Код:
[0] = ''
[1] = ''
 

asdf8

Скриптер
Сообщения
564
Репутация
152
firex [?]
Без регулярных выражений этот процесс слишком требователен к ресурсам. Файл порядка 1мб загружается около 20 секунд.
По правде говоря с трудом верится про 20 секунд.
Такие вложенные конструкции, используя только регэкспы, не распарсить.
 
Автор
firex

firex

AutoIT Гуру
Сообщения
943
Репутация
208
asdf8 [?]
По правде говоря с трудом верится про 20 секунд.
Собственно вот что я сделал на данный момент. Ситуацию осложняет ранее неизвестное количество вложенных секций.

Все это используется в рекурсивной конструкции. Может хотя бы часть можно перенести в регулярные выражения?

Код:
Func _Sfs_ReadSection( $sCode, $iMode = 0 )
	Local $_aCode = __Sfs_CodeToArray( $sCode ), $Idx

	Local $_aRet[1][2] = [[0]], $_iOpen = 0, $_aSection, $_sSection, $_iSize
	For $Idx = 1 To $_aCode[0] Step 1
		If Not $_iOpen Then
			$_aSection = StringRegExp( $_aCode[$Idx], '^([\w]+)$', 3 )
			If IsArray( $_aSection ) Then
				$_iSize = $_aRet[0][0] + 1

				ReDim $_aRet[$_iSize + 1][2]
				$_aRet[0][0] = $_iSize
				$_aRet[$_iSize][0] = $_aSection[0]

				$Idx += 1
				$_iOpen = 1
				$_sSection = ''
			EndIf
		Else
			If $_aCode[$Idx] = '{' Then
				$_iOpen += 1
			ElseIf $_aCode[$Idx] = '}' Then
				$_iOpen -= 1
				If Not $_iOpen Then
					$_aRet[$_aRet[0][0]][1] = StringTrimRight( $_sSection, 1 )
					ContinueLoop
				EndIf
			EndIf

			$_sSection &= $_aCode[$Idx] & @LF
		EndIf
	Next

	If $iMode Then _
		Return $_aRet[1][1]

	Return $_aRet
EndFunc

Func _Sfs_ReadKeys( $sCode )
	Local $_aCode = __Sfs_CodeToArray( $sCode ), $Idx

	Local $_aRet[1][2] = [[0]], $_aKey, $_iSize
	For $Idx = 1 To $_aCode[0] Step 1
		$_aKey = StringRegExp( $_aCode[$Idx], '([\w]+)\040=\040(.*)', 3 )

		If IsArray( $_aKey ) Then
			$_iSize = $_aRet[0][0] + 1
			ReDim $_aRet[$_iSize+1][2]

			$_aRet[0][0] = $_iSize
			$_aRet[$_iSize][0] = $_aKey[0]
			$_aRet[$_iSize][1] = $_aKey[1]
		Else
			Return $_aRet
		EndIf
	Next

	Return $_aRet
EndFunc
 

ivsatel

Продвинутый
Сообщения
319
Репутация
84
firex
Если я правильно понял, то приблизительно так:
Основано на полноценном примере.
VESSEL
{
pid = 05ef4c62f46944c7aff579f5056691c1
name = FXLM_b1
type = Ship
sit = LANDED
landed = True
landedAt =
splashed = False
met = 451843695.625706
lct = 493485.316783616
root = 0
lat = 2.42884140463921
lon = -158.170788381879
alt = 988.998613539094
hgt = 5.273009
nrm = 0.005769582,0.9999543,-0.007633958
rot = 0.7049874,-0.6646524,0.05567101,-0.2411032
CoM = -0.0002908502,-2.242053,0.0004771798
stg = 2
prst = False
ref = 4244007397
cPch = 0
cHdg = 0
cMod = 0
ORBIT
{
SMA = NaN
ECC = 1
INC = NaN
LPE = 163.796395124943
LAN = 0
MNA = -8.40714670366795
EPH = 459526557.867619
REF = 2
OBJ = 0
}
PART
{
name = Mark1-2Pod
uid = 4244007397
parent = 0
position = 0,0,0
rotation = 0,0,0,1
mirror = 1,1,1
istg = 0
dstg = 0
sqor = -1
sidx = -1
attm = 0
srfN = None, -1
attN = bottom, 2
attN = top, 1
mass = 4
temp = 0
expt = 0.5
state = 0
connected = True
attached = True
flag = Squad/Flags/hexagon
rTrf = Mark1-2Pod (FXLM_b1)
crew = 0
crew = 3
EVENTS
{
}
ACTIONS
{
}
MODULE
{
name = ModuleCommand
isEnabled = True
controlSrcStatusText = Operational
EVENTS
{
}
ACTIONS
{
}
}
RESOURCE
{
name = ElectricCharge
amount = 150
maxAmount = 150
flowState = True
flowMode = Both
}
}
}
Код:
Global $aArray[8] = [7, 'VESSEL', 'ORBIT', 'PART', 'EVENTS', 'ACTIONS', 'MODULE', 'RESOURCE']

$_sText = FileRead(@ScriptDir&'\file2.txt')

For $i = 1 To 7
	$_aResult =  StringRegExp( $_sText , '(?s)'&$aArray[$i]&'.*?\{{1,}.*?(\w.*?)\}.*?[A-Z]*?\{{0,1}', 3 )

	For $n = 0 To UBound($_aResult)-1
		ConsoleWrite($_aResult[$n]&@LF)
	Next
Next
 
Автор
firex

firex

AutoIT Гуру
Сообщения
943
Репутация
208
ivsatel
На первый взгляд похоже все работает просто великолепно!

Сейчас все это дело применю на своих функциях и попробую в деле.
 

asdf8

Скриптер
Сообщения
564
Репутация
152
firex [?]
На первый взгляд похоже все работает просто великолепно!
Попробуйте подсветить результаты, если пользуетесь SciTe, то так :
Код:
Global $aArray[8] = [7, 'VESSEL', 'ORBIT', 'PART', 'EVENTS', 'ACTIONS', 'MODULE', 'RESOURCE']

$_sText = FileRead(@ScriptDir & '\file2.txt')

For $i = 1 To 7
	ConsoleWrite('!>' & $aArray[$i] & @CRLF)
	$_aResult = StringRegExp($_sText, '(?s)' & $aArray[$i] & '.*?\{{1,}.*?(\w.*?)\}.*?[A-Z]*?\{{0,1}', 3)
	For $n = 0 To UBound($_aResult) - 1
		ConsoleWrite('->' & $_aResult[$n] & @CRLF)
	Next
Next



[?]
Если к примеру мы запрашиваем EVENTS, то должно вернуться:
Код:
[0] = ''
[1] = ''

а возвращается совсем другое.


[?]
Собственно вот что я сделал на данный момент.

первое, что бросается в глаза :
Код:
ReDim $_aRet[$_iSize+1][2]

с точки зрения быстродействия это реальный тормоз, лучше заменить на это :
Код:
If $_aRet[0][0] >= UBound($_aRet) - 1 Then ReDim $_aRet[2 * $_aRet[0][0]][2]
 
Автор
firex

firex

AutoIT Гуру
Сообщения
943
Репутация
208
asdf8 [?]
а возвращается совсем другое.
Да, в большинстве случаев возвращает верно, но не во всех. Уже проверил.

первое, что бросается в глаза :
Спасибо, подкорректировал.

OffTopic:
Только что сделал сохранение обратно в файл. То ли я намудрил с рекурсией(других вариантов нет), то ли что еще, AutoIt повесил мой i5 4.0ггц с 8 гб оперативы. Повесил он мгновенно, впервые такое вижу за ним. Первая моя реакция - удивление.


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

Закрывать тему думаю пока не стоит(один раз уже поспешил), может кто еще чего предложит, буду только рад :smile:
 

asdf8

Скриптер
Сообщения
564
Репутация
152
firex [?]
15 секунд на 1мб будет вполне не худший результат.

Все равно, это очень медленно, особенно если предположить, что это время одного прохода.
Вот код, который на 1мб делает 7 проходов примерно за секунду (без вывода в консоль):
Код:
Global $aArray[7] = ['VESSEL', 'ORBIT', 'PART', 'EVENTS', 'ACTIONS', 'MODULE', 'RESOURCE']

$_sText = FileRead(@ScriptDir & '\file2.txt')

For $i = 0 To UBound($aArray) - 1
	$aResult = GetSections($aArray[$i], $_sText)
	If IsArray($aResult) Then
		For $j = 0 To UBound($aResult) - 1
			ConsoleWrite('!> №' & String($j + 1) & @TAB & $aArray[$i] & @CRLF & $aResult[$j] & @CRLF)
		Next
	EndIf
Next

Func GetSections($sName, ByRef $sText)
	$pos = 1
	$aOut = StringRegExp($sText, '(?i)(?<!\w)(' & $sName & '\s*\{)', 3)
	If IsArray($aOut) Then
		For $i = 0 To UBound($aOut) - 1
			$pos = StringInStr($sText, $aOut[$i], 1, 1, $pos)
			If $pos Then
				$pos += StringLen($aOut[$i])
				$posOpen = $pos
				$posClose = $pos
				$iLevel = 1
				While 1
					$posOpen = StringInStr($sText, "{", 1, 1, $posOpen)
					$posClose = StringInStr($sText, "}", 1, 1, $posClose)
					If $posOpen And $posClose Then
						If $posOpen > $posClose Then
							$iLevel -= 1
							If $iLevel < 1 Then
								$aOut[$i] = StringMid($sText, $pos, $posClose - $pos)
								$aOut[$i] = StringRegExpReplace($aOut[$i], '(?s)^[\r\n]+(.*?)[\r\n]+\s*$', '\1')
								$pos = $posClose
								ExitLoop
							EndIf
							$posClose += 1
							$posOpen = $posClose
						Else
							$iLevel += 1
							$posOpen += 1
							$posClose = $posOpen
						EndIf
					ElseIf $posClose Then
						$iLevel -= 1
						If $iLevel < 1 Then
							$aOut[$i] = StringMid($sText, $pos, $posClose - $pos)
							$aOut[$i] = StringRegExpReplace($aOut[$i], '(?s)^[\r\n]+(.*?)[\r\n]+\s*$', '\1')
							$pos = $posClose
							ExitLoop
						EndIf
						$posClose += 1
					Else
						ConsoleWrite('!> ERROR' & @CRLF)
						ExitLoop
					EndIf
				WEnd
			Else
				$aOut[$i] = ''
			EndIf
		Next
	Else
		$aOut = 0
	EndIf
	Return $aOut
EndFunc

ps: код сильно не проверял.
 

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
Извините что вмешиваюсь... :-[
Переделал шаблон в скрипте asdf8
Код:
Global $aArray[8] = [7, 'VESSEL', 'ORBIT', 'PART', 'EVENTS', 'ACTIONS', 'MODULE', 'RESOURCE']

$_sText = FileRead(@ScriptDir&'\file2.txt')

For $i = 1 To 7
    $_aResult =  StringRegExp( $_sText , '(?sm)'&$aArray[$i]&".*?\{(.*?)^(?:[^\=]+?)$", 3 )
	ConsoleWrite($aArray[$i]&" :"&@LF)
    For $n = 0 To UBound($_aResult)-1
        ConsoleWrite($_aResult[$n]&@LF)
    Next
Next


Скорость проверить не на чем... :smoking:
 

asdf8

Скриптер
Сообщения
564
Репутация
152
C2H5OH [?]
Переделал шаблон в скрипте asdf8

Это не мой скрипт, а ivsatel

Использовать в данной задаче только регэкспы можно, но только если строго соблюдается одно условие : в каждой секции сначала идут пары ключ-значение, а только потом подсекции.
В приведенном топикстартером примере текста, это условие соблюдается, но насколько строго соблюдается это условие в общем случае - можно только гадать.
 

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
asdf8 :
только если строго соблюдается одно условие : в каждой секции сначала идут пары ключ-значение, а только потом подсекции


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

(сори за левый таг, но quote не поддерживает заголовок)
 

asdf8

Скриптер
Сообщения
564
Репутация
152
C2H5OH [?]
Если это условие не соблюдается, то получается что мы имеем какую-то рекурсивную структуру текста.
Это не так, потому что, даже при не выполнении данного условия имеется возможность однозначного определения всех ключей и подразделов для определенного раздела. Фактически формат очень похож на XML.
 

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
asdf8:
Фактически формат очень похож на XML.


Эээ, нет. В XML четко обозначено соответствие открывающего и закрывающего тэга. А здесь закрывающая скобка } соответствует непонятно какой открывающей { . Если бы эти фигурные скобки были идентифицируемы, то конечно всё было бы намного проще.
 
Автор
firex

firex

AutoIT Гуру
Сообщения
943
Репутация
208
Благодаря советам на счет ReDim я настолько подкорректировал код, что он теперь прогружает файл размером 5 мб за за секунд 6-8.

Спасибо всем.


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

Ах, совсем забыл.

C2H5OH
Ваше регулярное выражение я использую для нахождения редактированных секций(что бы не переписывать весь файл обратно). Этим я решил проблему со скоростью сохранения.

Спасибо еще раз всем.
 
Верх