Многострочное регулярное выражение Python + несколько записей, чтение файла за один раз

//Last modified: Sat, Apr 16, 2011 09:55:04 AM
//Codeset: ISO-8859-1
fileInfo "version" "20x64";
createNode newnode -n "a_SET";
    addAttr -ci true -k true -sn "connections" -ln "connections" -dt "string";
    setAttr -l on -k off ".tx";
    setAttr -l on -k off ".ty";
    setAttr -l on -k off ".sz";
    setAttr -l on -k on ".test1" -type "string" "blabla";
    setAttr -l on -k on ".test2" -type "string" "blablabla";
createNode newnode -n "b_SET";
    addAttr -ci true -k true -sn "connections" -ln "connections" -dt "string";
    setAttr -l on -k off ".tx";
    setAttr -l on -k off ".ty";
    setAttr -l on -k off ".sz";
    setAttr -l on -k on ".test1" -type "string" "hmm";
    setAttr -l on -k on ".test2" -type "string" "ehmehm";

в Python:

Мне нужно прочитать имена новых узлов, например «a_SET» и «b_SET», и соответствующие им значения атрибутов, так что {«a_SET»: {«test1»: «blabla», «test2»: «blablabla»} и то же самое для b_SET - могло быть неизвестное количество наборов - вроде c_SET d_SET и т. д.

Я пробовал перебирать строки и сопоставлять их там:

for line in fileopened:
    setmatch = re.match( r'^(createNode set -n ")(.*)(_SET)(.*)' , line)
     if setmatch:
            sets.append(setmatch.group(2))

и как только я нахожу здесь совпадение, я перебираю следующие строки, чтобы получить атрибуты (test1, test2) для этого набора, пока не найду новый набор - например, c_SET или EOF.

Как лучше всего получить всю эту информацию за один раз с помощью re.MULTILINE?


person tdgs    schedule 16.04.2011    source источник


Ответы (3)


Получил вот что:

import re

filename = 'tr.txt'

with open(filename,'r') as f:
    ch = f.read()

pat = re.compile('createNode newnode -n ("\w+?_SET");(.*?)(?=createNode|\Z)',re.DOTALL)
pit = re.compile('^ *setAttr.+?("[^"\n]+").+("[^"\n]+");(?:\n|\Z)',re.MULTILINE)

dic = dict( (mat.group(1),dict(pit.findall(mat.group(2)))) for mat in pat.finditer(ch)) 
print dic

результат

{'"b_SET"': {'".test2"': '"ehmehm"', '".test1"': '"hmm"'}, '"a_SET"': {'".test2"': '"blablabla"', '".test1"': '"blabla"'}}

.

Вопрос:

что, если в строках должен быть символ '"'? Как это представлено?

.

РЕДАКТИРОВАТЬ

Мне было трудно найти решение, потому что я не выбрал учреждение.

Вот новый шаблон, который улавливает ПЕРВУЮ строку "..." и ПОСЛЕДНЮЮ строку "...", присутствующую после строки " setAttr" и перед следующей " setAttr". Таким образом, может присутствовать несколько "...", а не только 3. Вы не спрашивали об этом условии, но я подумал, что оно может оказаться необходимым.

Мне также удалось сделать возможным наличие символов новой строки в строках, чтобы ловить "....\n......", а не только вокруг них. Для этого мне пришлось изобрести что-то новое: (?:\n(?! *setAttr)|[^"\n]) это означает: принимаются все символы, кроме '"' и обычных newlines \n, а также только символы новой строки, за которыми не следует строка, начинающаяся с ' *setAttr'

Для (?:\n(?! *setAttr)|.) это означает: за новой строкой не следует строка, начинающаяся с ' *setAttr' и всех остальных символов, отличных от новой строки.

Следовательно, любая другая специальная последовательность, такая как табуляция или что-то еще, автоматически принимается в сопоставлении.

ch = '''//Last modified: Sat, Apr 16, 2011 09:55:04 AM
//Codeset: ISO-8859-1
fileInfo "version" "20x64";
createNode newnode -n "a_SET";
    addAttr -ci true -k true -sn "connections" -ln "connections" -dt "string";
    setAttr -l on -k off ".tx";
    setAttr -l on -k off ".ty";
    setAttr -l on -k off ".sz";
    setAttr -l on -k on ".test1" -type "string" "blabla";
    setAttr -l on -k on ".test2" -type "string" "blablabla";
createNode newnode -n "b_SET";
    addAttr -ci true -k true -sn "connections" -ln "connections" -dt "string";
    setAttr -l on -k off ".tx";
    setAttr -l on -k off ".ty";
    setAttr -l on -k off ".sz";
    setAttr -l on -k on ".test1" -type "string" (
      "hmm bl
      abla\tbla" );
    setAttr -l on -k on ".tes\nt\t2" -type "string" "ehm\tehm";
    setAttr -l on -k on ".test3" -type "string" "too
    much" "pff" """ "feretini" "gol\nolo";
    '''

import re

pat = re.compile('createNode newnode -n ("\w+?_SET");(.*?)(?=createNode|\Z)',re.DOTALL)
pot = re.compile('^ *setAttr.+?'
                 '"((?:\n(?! *setAttr)|[^"\n])+)"'
                 '(?:\n(?! *setAttr)|.)+'
                 '"((?:\n(?! *setAttr)|[^"\n])+)"'
                 '.*;(?:\n|\Z)',re.MULTILINE)

dic = dict( (mat.group(1),dict(pot.findall(mat.group(2)))) for mat in pat.finditer(ch)) 
for x in dic:
    print x,'\n',dic[x],'\n'

результат

"b_SET" 
{'.test3': 'gol\nolo', '.test1': 'hmm bl\n      abla\tbla', '.tes\nt\t2': 'ehm\tehm'} 

"a_SET" 
{'.test1': 'blabla', '.test2': 'blablabla'}
person eyquem    schedule 16.04.2011
comment
Мне нравится подход - он работает хорошо. Я разместил там еще один вариант - как бы вы обошли эту возможность (когда строка прерывается символом '\ n' в начале строки? Спасибо! - person tdgs; 19.04.2011

Вы можете использовать положительный прогноз с регулярным выражением для разделения групп:

(yourGroupSeparator)(.*?)(?=yourGroupSeparator|\Z)

В вашем примере:

import re

lines = open("e:/temp/test.txt").read()
matches = re.findall(r'createNode newnode \-n (\"._SET\");(.*?)(?=createNode|\Z)', lines, re.MULTILINE + re.DOTALL);

for m in matches:
    print "%s:" % m[0], m[1]


"""
Result:
>>>
"a_SET":
    addAttr -ci true -k true -sn "connections" -ln "connections" -dt "string";
    setAttr -l on -k off ".tx";
    setAttr -l on -k off ".ty";
    setAttr -l on -k off ".sz";
    setAttr -l on -k on ".test1" -type "string" "blabla";
    setAttr -l on -k on ".test2" -type "string" "blablabla";

"b_SET":
    addAttr -ci true -k true -sn "connections" -ln "connections" -dt "string";
    setAttr -l on -k off ".tx";
    setAttr -l on -k off ".ty";
    setAttr -l on -k off ".sz";
    setAttr -l on -k on ".test1" -type "string" "hmm";
    setAttr -l on -k on ".test2" -type "string" "ehmehm";
"""

Если вам нужны результаты на диктовке, вы можете использовать:

result = {}
for k, v in matches:
    result[k] = v   # or maybe v.split() or v.split(";")

после findall

person PabloG    schedule 16.04.2011
comment
спасибо - мне нравится подход. Есть ли способ найти это в словаре? {'a_SET': [lines], 'b_SET':[nextlines]} и т. Д.? - person tdgs; 16.04.2011
comment
Если в RE нет ни '^', ни '&', ставить флаг re.MULTILINE бесполезно. - person eyquem; 16.04.2011
comment
@eyquem: Явное лучше, чем неявное;) - person PabloG; 16.04.2011
comment
Когда что-то подразумевается, это означает, что, хотя и невидимое, но полезно. Означает ли явное, что, хотя и видимое, оно может быть бесполезным? - person eyquem; 16.04.2011

Другой возможный вариант:

createNode newnode -n "b_SET";
    addAttr -ci true -k true -sn "connections" -ln "connections" -dt "string";
    setAttr -l on -k off ".tx";
    setAttr -l on -k off ".ty";
    setAttr -l on -k off ".sz";
    setAttr -l on -k on ".test1" -type "string" (
      "hmm blablabla" );
    setAttr -l on -k on ".test2" -type "string" "ehmehm";

Как видите, значение ".test1" теперь разделено разделителем строк / n. Как бы вы обошли это, используя подход Эйкема?

pit = re.compile('^ *setAttr.+?("[^"\n]+").+("[^"\n]+");(?:\n|\Z)',re.MULTILINE)
person tdgs    schedule 19.04.2011
comment
@eyquem, кроме того, там может быть разделитель табуляции / t, поэтому значение ключа "test1" станет "hmm blablabla", но теперь оно отделено от строки setAttr -l on -k on ".test1" -type "string" \n и, возможно, одной или двумя \t табуляторами - person tdgs; 19.04.2011
comment
@eyquem Спасибо за быстрый ответ! Ценить! Я пробовал это, и он отлично работает в приведенном выше примере, но не работает в приведенном ниже - знаете ли вы, чего мне здесь все еще не хватает? Немного потеряно на данный момент. - person tdgs; 20.04.2011