substituir vários vírgula fora lado um ou mais conjuntos de chaves e exceção que em um ou mais conjuntos de chaves

2

No arquivo de texto, tenho vários registros. Cada registro tem várias colunas separadas por vírgulas, algumas colunas têm um conjunto de chaves e outras têm mais de uma chave.

Eu preciso:

  1. Se uma vírgula for encontrada fora de um ou mais conjuntos de chaves, o vírgula deve ser substituída por um tubo.

  2. Se uma vírgula for encontrada dentro de um ou mais conjuntos de chaves, a vírgula deve ser deixada em paz. Então, dado THING1,{THING2,{THING3,}},THING4 , a saída deve ser THING1|{THING2,{THING3,}}|THING4 .

Registro de amostra:

(999969,2500,"777777888",0,"45265","65522",NULL,10001,2014-09-15 10:27:07.287,2014-09-15 10:28:49.085,2014-09-15 06:28:50.000,0,0,NULL,"text","401c4133091977",{F,F,"711592473,"00967711580001,F,NULL,NULL,"421010617759466","'401c4133091977H'",NULL,NULL,NULL,NULL,NULL,NULL,1,1,10,1,0,0,0,"a30200000000276f",NULL},NULL,{gggg{-1, 0, -1, 1410762530000, 87, 0, 0}, rrrr[{"foot", 24000, 976000, 3999-12-31 23:59:59, 0}], rrrr[{1000003, 1410762443000, 120, 87, 0, 0, 2, 1, 24000, 0, 0}]},{dd=0, ff=0, gg=0, hh=1, ctr="live", dddd="52265", eni=55, cuc=1},NULL,NULL,NULL,NULL,NULL,{NULL,NULL,NULL,0,"wwww","eeee",2014-10-10 10:45:59.000,2015-03-09 23:59:59.000,2015-06-07 23:59:59.000,2015-08-06 23:59:59.000,NULL})

o resultado deve ser:

(**999969|2500|"777777888"|0|"45265"|"65522"|NULL|10001|2014-09-15 10:27:07.287|2014-09-15 10:28:49.085|2014-09-15 06:28:50.000|0|0|NULL|"text"|"401c4133091977"|**{F,F,"711592473,"00967711580001,F,NULL,NULL,"421010617759466","'401c4133091977H'",NULL,NULL,NULL,NULL,NULL,NULL,1,1,10,1,0,0,0,"a30200000000276f",NULL}**|NULL|**{gggg{-1, 0, -1, 1410762530000, 87, 0, 0}, rrrr[{"foot", 24000, 976000, 3999-12-31 23:59:59, 0}], rrrr[{1000003, 1410762443000, 120, 87, 0, 0, 2, 1, 24000, 0, 0}]}**|**{dd=0, ff=0, gg=0, hh=1, ctr="live", dddd="52265", eni=55, cuc=1}**|NULL|NULL|NULL|NULL|NULL|**{NULL,NULL,NULL,0,"wwww","eeee",2014-10-10 10:45:59.000,2015-03-09 23:59:59.000,2015-06-07 23:59:59.000,2015-08-06 23:59:59.000,NULL})
    
por M.H.A 09.10.2014 / 11:56

4 respostas

4

Você pode fazer isso simplesmente através da combinação Perl + regex .

perl -pe 's/(\{(?:[^{}]|(?1))*\})(*SKIP)(*F)|,/|/g' file

Exemplo:

$ perl -pe 's/(\{(?:[^{}]|(?1))*\})(*SKIP)(*F)|,/|/g' file
(999969|2500|"777777888"|0|"45265"|"65522"|NULL|10001|2014-09-15 10:27:07.287|2014-09-15 10:28:49.085|2014-09-15 06:28:50.000|0|0|NULL|"text"|"401c4133091977"|{F,F,"711592473,"00967711580001,F,NULL,NULL,"421010617759466","'401c4133091977H'",NULL,NULL,NULL,NULL,NULL,NULL,1,1,10,1,0,0,0,"a30200000000276f",NULL}|NULL|{gggg{-1, 0, -1, 1410762530000, 87, 0, 0}, rrrr[{"foot", 24000, 976000, 3999-12-31 23:59:59, 0}], rrrr[{1000003, 1410762443000, 120, 87, 0, 0, 2, 1, 24000, 0, 0}]}|{dd=0, ff=0, gg=0, hh=1, ctr="live", dddd="52265", eni=55, cuc=1}|NULL|NULL|NULL|NULL|NULL|{NULL,NULL,NULL,0,"wwww","eeee",2014-10-10 10:45:59.000,2015-03-09 23:59:59.000,2015-06-07 23:59:59.000,2015-08-06 23:59:59.000,NULL})

Explicação:

Eu divido o regex em duas partes para a explicação.

  1. (\{(?:[^{}]|(?1))*\})
  2. (*SKIP)(*F)|,

1ª parte

(\{(?:[^{}]|(?1))*\})
  • Esse truque funcionará somente se as chaves forem adequadamente pareadas.
  • () Estes são grupos de captura, usados para capturar caracteres.
  • \{ corresponde a uma chave de abertura.
  • (?:[^{}]|(?1))

    • (?:...) Chamado grupo sem captura.
    • [^{}] Corresponderá a qualquer caractere, mas não a { ou }
    • | operador OR lógico.
    • (?1) Recorre ao primeiro grupo de captura.
  • (?:[^{}]|(?1))* Corresponde ao token anterior zero ou mais vezes.
  • \} Fechando o símbolo } .

Considere o exemplo abaixo e o padrão que corresponde aos colchetes aninhados.

String:

h{foo{bar}foobar}

Padrão:

h(\{(?:[^{}]|(?1))*\})
  • No início, o mecanismo de regex tenta corresponder o h ( que estava no padrão ) à cadeia de entrada. Então a primeira letra h foi correspondida.
  • O padrão para encontrar os parênteses equilibrados é inserido em um grupo de captura.
  • Agora, o mecanismo pega o segundo caractere (isto é, \{ ) no padrão e tenta corresponder à string de entrada. Então o primeiro { foi capturado . Eu usei a palavra capturada em vez da correspondência porque \{ está dentro de um grupo de captura.
  • (?:[^{}]|(?1))* Indica ao mecanismo de expressão regular que corresponda a qualquer caractere, exceto { ou } zero ou mais vezes. Se você encontrou qualquer caractere { ou } , recrie novamente o primeiro grupo de captura. Então agora a string foo foi capturada. O caractere a seguir é { , então ele recursiva para o primeiro grupo de captura. Agora, o mecanismo regex está um nível abaixo na recursão. Qual é o primeiro padrão no nosso primeiro grupo de captura ( veja o regex )? É \{ , agora corresponde ao símbolo { que foi logo após a string foo .
  • O mecanismo ainda tem um nível de recursão, novamente o padrão (?:[^{}]|(?1))* corresponde à sequência bar . Agora, o caractere após o bar é } , portanto, depois de corresponder à string bar , o mecanismo regex não entrará em (?1) . Por isso, fizemos o grupo que não captura para repetir zero ou mais vezes. O próximo padrão ( padrão após a (?:[^{}]|(?1))* ) na regex é \} . Portanto, esse \} corresponderia à } brace que estava logo após a bar . Agora, o mecanismo regex sai de um nível de recursão e o padrão [^{}]* corresponderia à seguinte string foobar . O último \} corresponderia ao último colchete.
  • Agora, nosso primeiro grupo de captura contém {foo{bar}foobar} .

2ª parte

  • (*SKIP)(*F) Faz com que os caracteres que são correspondidos ou capturados falhem. Então, no nosso caso, todas as chaves balanceadas capturadas foram ignoradas. Ou seja, ele força o mecanismo de expressão regular a corresponder aos caracteres da sequência restante.
  • Sintaxe ou formato de (*SKIP)(*F)

        part1(*SKIP)(*F)|part2
         |                  |
     |----                  -----> Match this
    Don't match this 
    
  • Portanto, o padrão que estava logo após o | tentará corresponder os caracteres da string restante ( string, exceto as chaves aninhadas ).

  • No nosso caso, o padrão após o | é , . Então, todas as vírgulas que estão fora das chaves aninhadas foram combinadas.

Leia este para entender o Regular Expression Recursion .

Nota:

  • (?R) recursa todo o sub-padrão, ou seja, toda a correspondência. Também poderíamos escrever (?R) as (?0)
  • (?1) recurses no primeiro subpadrão (ou seja, padrão dentro do primeiro grupo de captura)
por 13.10.2014 / 11:21
0

substituir

,{ 

com

|{ 

e

}, 

com

}|

 echo "THING1,{THING2,{THING3,}},THING4" | sed -re "s/,\{/|{/gi" | sed -re "s/},/}|/gi"

resulta em

THING1|{THING2|{THING3,}}|THING4
    
por 09.10.2014 / 14:10
0

Não seja informado que é uma declaração sed . Mas, deve respeitar em cascata. Aqui está em uma linha:

sed -e 's/,/|/g;:a;s/{\([^{}]*\)|\([^{}]*\)}/{,}/g;ta;s/{\([^{}]*\)}/<>/g;ta;:b;s/<\([^<>]*\)>/{}/g;tb' file

Aqui está a versão comentada:

sed -e '
        s/,/|/g;                                 #replaces all commas (,) with pipes (|)
        :a;                                      #sets a label called a
            s/{\([^{}]*\)|\([^{}]*\)}/{,}/g; #replaces {a|b|c} with {a,b|c}
          ta;                                    #go back to the label 'a' and repeat the
                                                 #prevous part until there is nothing more
                                                 #to replace: when {a|b|c} became {a,b,c}
          s/{\([^{}]*\)}/<>/g;                 #replace {...} with <...>
        ta;                                      #go back to label a again until all {} are 
                                                 #replaces by <>
        :b;                                      #create a new label called b
          s/<\([^<>]*\)>/{}/g;                 #replace <...> back to {...}
        tb;                                      #and back to label b to repeat the previous
                                                 #part
' file

Com isso, consegui a saída desejada.

    
por 09.10.2014 / 14:33
0

Eu criei algumas maneiras de fazer isso em sed , mas a maioria quebrou os casos. Um, no entanto, não faz:

sed 's/^/\n/;:b
/\n\n/!s/\(\n[^,{}]*\),/|/;tb
s/\(\n\n*\)\([^{}]*[{}]\)//
s/{\(\n\)/&/;s/\(}\n\)\n//;tb
s/\n//g' <<\DATA
(999969,2500,"777777888",0,"45265","65522",NULL,10001,2014-09-15 10:27:07.287,2014-09-15 10:28:49.085,2014-09-15 06:28:50.000,0,0,NULL,"text","401c4133091977",{F,F,"711592473,"00967711580001,F,NULL,NULL,"421010617759466","'401c4133091977H'",NULL,NULL,NULL,NULL,NULL,NULL,1,1,10,1,0,0,0,"a30200000000276f",NULL},NULL,{gggg{-1, 0, -1, 1410762530000, 87, 0, 0}, rrrr[{"foot", 24000, 976000, 3999-12-31 23:59:59, 0}], rrrr[{1000003, 1410762443000, 120, 87, 0, 0, 2, 1, 24000, 0, 0}]},{dd=0, ff=0, gg=0, hh=1, ctr="live", dddd="52265", eni=55, cuc=1},NULL,NULL,NULL,NULL,NULL,{NULL,NULL,NULL,0,"wwww","eeee",2014-10-10 10:45:59.000,2015-03-09 23:59:59.000,2015-06-07 23:59:59.000,2015-08-06 23:59:59.000,NULL},poopoer,sciioooper)
DATA

Isso percorre uma linha de dados usando um delimitador que você pode ter certeza de não encontrar em uma linha - um caractere de nova linha. Ele percorre a linha da esquerda para a direita, parando no próximo de um dos dois pontos de interesse - os caracteres }{ . Quando ele parar em { , ele adicionará um caractere de nova linha ao seu delimitador; quando a } subtrai uma se duas são necessárias.

Quando ele é parado em um ponto no qual há apenas um caractere de nova linha a ser encontrado na linha e uma vírgula segue seu delimitador antes de um {} , ele irá substituí-lo por um pipe e recuar para tentar o mesmo teste de substituição novamente.

Isso deve proteger até grupos de contraventamento desequilibrados, se necessário, embora não empregue nenhum método de manipulação de uma chave entre aspas, o que pode ser feito adicionando pontos de interesse , mas eu sou não muito animado em descobrir.

A saída da sua amostra:

(999969|2500|"777777888"|0|"45265"|"65522"|NULL|10001|2014-09-15 10:27:07.287|2014-09-15 10:28:49.085|2014-09-15 06:28:50.000|0|0|NULL|"text"|"401c4133091977"|{F,F,"711592473,"00967711580001,F,NULL,NULL,"421010617759466","'401c4133091977H'",NULL,NULL,NULL,NULL,NULL,NULL,1,1,10,1,0,0,0,"a30200000000276f",NULL}|NULL|{gggg{-1, 0, -1, 1410762530000, 87, 0, 0}, rrrr[{"foot", 24000, 976000, 3999-12-31 23:59:59, 0}], rrrr[{1000003, 1410762443000, 120, 87, 0, 0, 2, 1, 24000, 0, 0}]}|{dd=0, ff=0, gg=0, hh=1, ctr="live", dddd="52265", eni=55, cuc=1}|NULL|NULL|NULL|NULL|NULL|{NULL,NULL,NULL,0,"wwww","eeee",2014-10-10 10:45:59.000,2015-03-09 23:59:59.000,2015-06-07 23:59:59.000,2015-08-06 23:59:59.000,NULL}|poopoer|sciioooper)
    
por 13.10.2014 / 20:00