extração de correspondência e substring de regex

1

Eu tenho esses arquivos, criados manualmente por muitas pessoas diferentes. A formatação, apesar de seguir uma determinada regra, não é uniforme.

pense nessas três linhas abaixo

"erroneous_data_F08R16_recordeded_by_tech21"
"erroneous_data_F8R16_recordeded_by_tech021"
"erroneous_data_F008R016_recordeded_by_tech21"

todos eles apontam para a mesma coisa F008 ou F08 ou F8 significa o número de arquivo 8 R16 ou R016 ou (R [dígito único] sempre que possível) significa o número de linha 16

Há um número dessas linhas em um determinado arquivo, que será verificado usando while read line loop.

O que eu quero fazer é tornar a seção de número de arquivos e linhas uniforme, como F008R016 para as três linhas de exemplos acima, pois meus números de arquivo não têm mais de 3 dígitos (ele passa depois de 999 e número de linhas nunca são mais do que um punhado em cada arquivo, mas por uma questão de consistência, digamos que seja de 3 dígitos.Neste arquivo eu preciso processar, também existem comentários não estruturados.Então, primeira ordem de negócio é detectar as linhas e separar -los para um arquivo temporário diferente, em seguida, tornando-os uniformes.

Para conseguir isso, meu plano é fazer eco da linha e do grep para regex que correspondam ao padrão. Infelizmente, o regex não é o meu ponto strong.

Até agora eu estou preso na detecção do arquivo # row # estrutura na linha

cat InputFile | while read line
do
  echo $line | grep '[F,f]\d\d[R,r]\d\d' >/dev/null  #this is assuming two digit file number and 2 digit row number 
  result=$?
  if [ $result -eq 0 ]
  then
    echo $line >tempfile
  fi
done

essa regex correspondente no comando grep falha o tempo todo, mesmo que a linha contenha o padrão F08R16.

Depois de realizar isso, quero extrair essa substring em uma variável e analisar a estrutura da variável e adicionar zeros à esquerda, quando necessário, para torná-la uniforme.

Qualquer sugestão para corrigir meu regex e atingir meu objetivo maior de extrair variáveis é muito apreciada.

Para o que vale a pena, estou trabalhando em uma caixa de versão 6.7 do CentOS na época, mas tenho outras distribuições à minha disposição.

    
por Scott 06.01.2016 / 16:51

3 respostas

3

Suponho que você deseja corresponder um f ou um F , em seguida, 1, 2 ou 3 números seguidos por um r ou R e, em seguida, 1, 2 ou 3 números até que _ . Se sim, você pode fazer (com GNU grep ):

grep -iP 'f\d{1,3}r\d{1,3}_' InputFile > tmpfile

Ou com o não-GNU grep :

grep -iE 'f[0-9]{1,3}r[0-9]{1,3}_' InputFile > tmpfile

No entanto, isso é quase certamente um problema XY . Você realmente não quer fazer esse tipo de coisa no shell. Por exemplo, esse perl one-liner formatará todas as linhas relevantes corretamente:

$ perl -pe 's/_f(\d+)r(\d+)_/sprintf("_F%03dR%03d_",$1,$2)/ei' file
"erroneous_data_F008R016_recordeded_by_tech21"
"erroneous_data_F008R016_recordeded_by_tech021"
"erroneous_data_F008R016_recordeded_by_tech21"

Isso é apenas para dar uma ideia do tipo de truque que você pode usar para evitar esse tipo de problema.

    
por 06.01.2016 / 17:15
2

Não echo em grep assim - isso é loucura.

<infile grep -iE '([fr][0-9]+){2}' >outfile

... você deve obter as linhas que você está perguntando. Chamando cat para escrever um arquivo no seu shell através de um pipe que você então read byte para byte que você copia em outro pipe depois de interpretar e elidir vários caracteres de sintaxe do shell com echo byte para byte para que você possa silenciosamente grep esses bytes para o sucesso ... bem ...

grep apenas escreverá as correspondências para você. Se você quiser uma contagem de linhas correspondentes ou algo assim, use -c . Se você quiser que os números de linha para linhas correspondentes usem -n . Se você quiser correspondências que não diferenciam maiúsculas de minúsculas, use -i . Talvez tente man grep para mais.

Para editar ao vivo o fluxo que você pode usar sed :

sed -Ee:t -e's/((_)[Ff]|[0-9]{3,}[Rr])([0-9]{1,2}(|[Rr]))//g;tt'

Você precisará de um GNU / BSD / AST sed para que isso funcione. Mas funciona muito bem:

sed -Ee:t -e's/((_)[Ff]|[0-9]{3,}[Rr])([0-9]{1,2}(|[Rr]))//g;tt' \
<<""
"erroneous_data_F08R16_recordeded_by_tech21"
"erroneous_data_F8R16_recordeded_by_tech021"
"erroneous_data_F008R016_recordeded_by_tech21"
"erroneous_data_F008R016_recordeded_by_tech21"
"erroneous_data_F008R016_recordeded_by_tech021"
"erroneous_data_F008R016_recordeded_by_tech21"

Você não é o primeiro cara a vir reclamando sobre essa tecnologia 21 também. Alguém deveria endireitar esse cara.

    
por 06.01.2016 / 17:04
0

A resposta perl de terdon é certamente elegante e eu concordo: se o objetivo é tornar todos os dados formatados de maneira uniforme / consistente, não há necessidade de separar as linhas que precisam ser alteradas. Caso você não goste de perl (ou no caso improvável de você não tê-lo), aqui está uma solução sed :

sed -re 's/_[Ff]([0-9]+)[Rr]([0-9]+)_/_F00R00_/' \
                                          -e 's/_F0*([0-9]{3})R0*([0-9]{3})_/_FR_/'

Isso pode ser digitado como uma única linha (deixe de fora o \ no final da primeira linha). Eu admito, isso não é tão elegante quanto a solução perl . Funciona em dois passos:

  • Adicione 00 após cada F ou R (ou f ou r ) no %código% padronizar. Isso altera um dígito _ F file_number R file_number _ para 8 , dois dígitos 008 para 08 , e três dígitos 0008 to 008 .
    (A primeira etapa também capitaliza 00008 ou f .)
  • Após cada r ou F no %código% padrão, exclua quantos zeros aparecerem antes dos três últimos dígitos. Portanto, R é deixado em paz, enquanto _ F file_number R file_number _ e 008 são alterados para 0008 .

Se a sua versão de 00008 não suportar a opção 008 (use expressões regulares estendidas), use

sed -e 's/_[Ff]\([0-9]*\)[Rr]\([0-9]*\)_/_F00R00_/' \
                                          -e 's/_F0*\([0-9]{3}\)R0*\([0-9]{3}\)_/_FR_/'

usando sed em vez de -r e \(…\) em vez de (…) . ( * e + não significam a mesma coisa, mas eles são próximos o suficiente neste caso, a menos que haja linhas com strings como * ou + . De fato, você poderia usar _FR42_ em vez de _F17R_ no primeiro comando também.)

Como usar estes

  • *
    ou +
    para processar o arquivo de entrada e ver os resultados na tela.
  • sed option(s) scripts InputFile
    ou sed option(s) scripts < InputFile
    para processar o arquivo de entrada e enviar os resultados para um novo arquivo.
  • sed option(s) scripts InputFile > output_file
    processar o arquivo e modificá-lo no local; ou seja, envie os resultados de volta para o arquivo original.
por 06.01.2016 / 19:51