Grupo correspondente aninhado na regex


8

Eu tenho um caso de usuário comum quando transformo alguma expressão python da seguinte maneira:

value 1
value 2
value 3

para dentro

['value 1', 'value 2', 'value 3']

A maneira mais fácil pode ser usar um mapeamento, mas eu queria usar uma substituição para esta tarefa.

Até agora eu tenho:

s/\(.*\n\)\+/[&]/g

Que resultam em

[value 1
value 2
value 3
]

Isso levanta uma questão, porque eu quero poder combinar o \(.*\), mas não \noe usar o resultado do correspondente dentro de a '...'.

Você sabe como fazer isso?


2
Não sei como fazer isso em uma única substituição, mas você pode fazê-lo em 2 enquanto estiver no modo visual (após selecionar a expressão python): :'<,'>s/\v(.*)\n/'\1', / | s/\v(.*), /[\1]/Você pode transformar isso em um mapeamento visual: xnoremap ,x :s/\v(.*)\n/'\1', / <Bar> s/\v(.*), /[\1]/<CR>e talvez em um mapeamento normal, se o A expressão está dentro de um parágrafo: nnoremap ,x :'{+1,'}-1s/\v(.*)\n/'\1', / <Bar> s/\v(.*), /[\1]/<CR>aqui estaria o mapeamento ,x.
User9433424

1
Não poderia fazer com regex, mas usando comandos externos:%! echo "[$(sed "s/.*/'&',/" % | tr '\n' ' ' | sed 's/, $//')]"
Sundeep

Respostas:


5

Editar

É possível fazer isso em uma expressão se usarmos uma "sub-substituir-expressão". Veja abaixo para obter informações sobre isso.

/Editar

O problema aqui é que você deseja fazer duas coisas diferentes.

  1. Opere a partida como um todo (ex .: envolva-a com [])

  2. Opere em cada item da partida (ex .: envolva-os com '',)

Você pode facilmente fazer qualquer um:

  1. :s/\(.\+\n\)\+/[&]/
  2. :%s/\(.\+\)\n/'\1', /

mas, tanto quanto sei, não há como fazer as duas coisas em uma única operação. Eu tentei obter a saída adequada com algo como:

:s/\(\(.\+\)\n\)\+/[\2]/

Mas é claro que o problema disso é que as \2correspondências apenas a última correspondência do segundo conjunto de parênteses de memória \(\)e não "lembram" nada antes disso. Então você acaba com apenas a última linha.

Eu recomendaria fazer um pré / pós-processamento com um :s///comando adicional para se livrar das novas linhas antes / depois do fato. Aqui está o que eu vim com

function! FormatExpression()
   .,/\n^$/s/\(.*\)\n/'\1', /
   s/\(.*\), /[\1]/
endfunction

1ª linha (Remover novas linhas)

  • .,/\n^$/Este é um modificador de intervalo para a pesquisa e substituição. Sem isso, o comando continuará mutilando seu arquivo inteiro. Atualmente, passa da linha atual .para a próxima linha em branco \n^$. Não tenho certeza de como você pretendia dividir as coisas, mas você precisa de uma maneira de dizer para parar.
  • s/ O início de um comando de pesquisa e substituição
  • \(.*\)\n Corresponda a linha inteira, mas salve a peça apenas sem a nova linha.
  • '\1', Substitua a linha pela correspondência entre aspas simples e acrescente uma vírgula.

2ª linha (entre parênteses)

  • \(.*\), Corresponda a linha inteira, mas não a última vírgula e espaço
  • [\1] Coloque entre parênteses e também remova vírgula e espaço supérfluos.

Vou continuar investigando isso, mas no momento não acho que seja possível com uma única expressão. :(

EDITAR:

Eu encontrei uma maneira de fazer isso com uma expressão! Internamente, na verdade, são duas substituições, mas tecnicamente é uma expressão. Aqui está o que eu vim com:

:s/\v((.+\n)*.+)\n/\= "['" . substitute(submatch(1), '\n', "', '", 'g') . "']" /
  • :s///: Fazer uma substituição
  • \v((.+\n)*.+)\n: Basicamente reúne todas as próximas linhas não em branco e armazena tudo, exceto a final \n
  • \=Permite usar uma expressão na substituição (consulte :h sub-replace-expression)
  • substitute(submatch(1)...): Substitui todos os armazenados \npor', '
  • "['" . ... . "']": Anexa ['e anexa']

Isso começará na posição do cursor e continuará até encontrar uma linha em branco ( ^\n). Não agarrar o último \né importante, pois sem esse pedaço ficamos com um adicional ',que não queremos no final.

Alguns podem considerar isso mais complexo do que a resposta de duas expressões anterior. Mas pensei em ir em frente e adicionar isso, já que é de fato possível fazê-lo com uma expressão. :)


2

Destaque visualmente, então:

:'<,'> s/.*/['&']/ | *j! | s/]\[/, /ge

Ele circunda cada linha, para fazer ['value 1'], por exemplo , une todas elas, depois substitui as adjacentes ]e [por vírgula.

A documentação para o *in *j!é :help cpo-star, a propósito. Isso é um pouco difícil de encontrar.


Em torno do trabalho agradável :)
nobe4

Na verdade, você pode usar :'<,'>s/\v(.*)(\_.)/['\1']/e remover a união.
nobe4

Sim, mas come a final \n, é por isso que eu usei :join. Eu provavelmente deveria ter mencionado isso. :-)
Antony

1
Que tal '<,'>s/.*/['&']/ | *s/]\_.\[/, /então?
nobe4

1
Sim, está melhor. Embora eu provavelmente escrevesse a segunda parte como *s/]\n\[/, /e.
Antony
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.