Substituir caractere especificado entre duas seqüências de caracteres?

1

eu tenho algo para realizar. Preciso substituir toda a ocorrência de & dentro ou entre <ex> </ex> to #. exemplo real abaixo:

a & b & c <ex> a & b & c </ex> a & b & c

novamente, preciso substituir toda a ocorrência de & dentro de <ex> e antes de </ex>

resultado esperado:

a & b & c <ex> a # b # c </ex> a & b & c

por favor poste a explicação também sobre como vocês conseguiram fazer isso.

EDIT # 1

Por favor, me forneça apenas uma solução sed , já que vou executar isso em um sistema AS400 e não tenho a capacidade de instalar o Perl ou qualquer outro intérprete.

    
por kemaro 28.08.2014 / 16:49

4 respostas

6

Se houver apenas uma ocorrência de <ex>...</ex> por linha:

sed -e :1 -e 's@\(<ex>.*\)&\(.*</ex>\)@#@;t1'

Se houver várias ocorrências e elas não aninharem (ou aninhem-se e você queira substituir o & apenas nas ocorrências mais profundas):

sed '
  s|_|_u|g        # replace all underscores with "_u"
  s|(|_o|g        # replace all open parentheses with "_o"
  s|)|_c|g        # replace all close parentheses with "_c"
  s|<ex>|(|g      # replace all open ex tags with "("
  s|</ex>|)|g     # replace all close ex tags with ")"

  :1              # a label

  s/\(([^()]*\)&\([^()]*)\)/#/g
                  # find:
                  #   an open parentheses, 
                  #   some non-parentheses chars (captured),
                  #   an ampersand, 
                  #   some non-parentheses chars (captured) and 
                  #   a close parentheses, 
                  # replace with
                  #   the first captured text, 
                  #   an octothorpe
                  #   the second captured text, 
                  # globally in the current record.

  t1              # if there was a successful replacement, goto label "1",
                  # else carry on

  s|(|<ex>|g      # restore open tags
  s|)|</ex>|g     # restore close tags
  s|_o|(|g        # restore open parentheses
  s|_c|)|g        # restore close parentheses
  s|_u|_|g        # restore underscores
'

Se eles podem aninhar e você deseja substituir nos seguintes:

sed '
  s|_|_u|g;s|(|_o|g;s|)|_c|g
  s|<ex>|(|g;s|</ex>|)|g;:1
  s/\(([^()]*\)(\([^()]*\))\([^()]*)\)/_O_C/g;t1
  :2
  s/\(([^()]*\)&\([^()]*)\)/#/g;t2
  s|(|<ex>|g;s|)|</ex>|g
  s|_O|<ex>|g;s|_C|</ex>|g
  s|_o|(|g;s|_c|)|g;s|_u|_|g'
    
por 28.08.2014 / 18:03
2

Perl (versão 5.14 necessária) para o resgate:

perl -pe 's%(<ex>.*?</ex>)% $1 =~ s/&/#/gr %eg'

Nas versões mais antigas, você precisa ser mais detalhado:

perl -pe 's%(<ex>.*?</ex>)% ($_x = $1) =~ s/&/#/g; $_x %eg'

Explicação: leve tudo entre as tags <ex> para $1 e, dentro de $1 , substitua & por #.

    
por 28.08.2014 / 16:54
2

Outro comando perl,

$ perl -pe 's/&(?=(?:(?!<ex>|<\/ex>).)*<\/ex>)/#/g' file
a & b & c <ex> a # b # c </ex> a & b & c

Antes de explicar o comando acima, vou explicar o que, na verdade, um lookahead negativo e lookahead positivo farão.

Em um regex (?=...) significa uma visão antecipada positiva. lookarounds (isto é, lookahead positivo e negativo, lookbehinds positivos e nagativos) faria uma correspondência de largura zero. Ou seja, não corresponderá a nenhum caractere. Normalmente lookaheads positivos e negativos são usados para fins de verificação de condições. E também (?:...) são chamados de grupos sem captura. Ou seja, o padrão dentro do grupo de não captura fará apenas uma operação de correspondência. Não vai capturar nenhum personagem.

  • (?!<ex>|<\/ex>) Impossível corresponder as strings <ex> ou </ex> .
  • (?:(?!<ex>|<\/ex>).) O que isso realmente significa, primeiro, procura os três caracteres ou quatro caracteres a seguir e garante que os três ou quatro caracteres a seguir não sejam <ex> ou </ex> . Corresponde ao seguinte caractere . apenas se essa condição for satisfeita.
  • (?:(?!<ex>|<\/ex>).)* Realiza a etapa acima, zero ou mais vezes até que a string <ex> ou </ex> seja detectada. Depois de encontrar qualquer uma dessas duas cadeias, de repente, ela pára de corresponder aos seguintes caracteres.

  • (?:(?!<ex>|<\/ex>).)*<\/ex> Corresponde à seguinte </ex> string também. Esse todo foi dado à antecipação.

  • &(?=(?:(?!<ex>|<\/ex>).)*<\/ex>) Finalmente, ele corresponde ao caractere & apenas se for seguido pelos caracteres que satisfazem a condição mencionada acima. ou seja, & deve ser seguido por qualquer caractere que não seja de <ex> ou </ex> zero ou mais vezes seguido por uma tag de fechamento </ex>

por 28.08.2014 / 16:55
0

Talvez isso possa ajudar, se você tiver mais de uma ocorrência na linha, não aninhando:

#cat plop
>a & b & c <ex> a & b & c </ex> a & b & c <ex> a & b & c </ex> a & b & c

#cat plop |sed -e :1 -e 's@\(<ex>[^(</ex>)]*\)&\(.*</ex>\)@+@;t1'     
>a & b & c <ex> a + b + c </ex> a & b & c <ex> a + b + c </ex> a & b & c
    
por 12.08.2016 / 18:53

Tags