Expressão regular para corresponder às aspas duplas de abertura faltando aspas duplas finais

Expressão regular para corresponder às aspas duplas de abertura faltando aspas duplas finais

Eu tenho um arquivo de dados grande (25 milhões de linhas) |delimitado por barras verticais (). O fornecedor de dados fornece arquivos e eu executo trabalhos automatizados para carregar os arquivos em um banco de dados Redshift e depois processar os dados.

A seguir está uma amostra dos dados:

123|110092|ACCT|"HC Account"|"Account1||||||||||||"Mary"|||"|"||||132|"STE|504"|1253|Unspecified Account|||N||ACTV|Active||||04/30/2013|12/31/2099|||||||||||||

Existem três conjuntos de dados de campo que vi até:

  1. Um campo de texto é colocado entre aspas duplas ( "). Por exemplo: "HC Account", "Mary", e "|". Isso está correto e os dados devem ser carregados sem as aspas.
  2. Alguns valores conterão o delimitador de barra vertical. Por exemplo: "STE|504". Neste caso, o campo deve necessariamente estar entre aspas duplas. Se não for, cai na categoria três abaixo.
  3. Às vezes, apenas uma cotação inicial é fornecida e não há cotação final. Por exemplo: "Account1.

TL;DR: Qualquer campo que comece com |", deve terminar com "|. Caso contrário, e outro |"for encontrado, a primeira aspa dupla deverá ser escapada.

Portanto, minha linha de dados deve ser editada para se tornar a seguinte depois de pré-processá-la em Unix/Python/outras sugestões:

123|110092|ACCT|"HC Account"|"Account1||||||||||||"Mary"|||"|"||||132|"STE|504"|1253|Unspecified Account|||N||ACTV|Active||||04/30/2013|12/31/2099|||||||||||||


Estou planejando escrever um script Unix para modificar o arquivo usando SED. A expressão regular que escrevi até agora é:

(\|")(?!([a-zA-Z0-9]|\s|\||\/)*("\|))

No entanto, isso não corresponde à string corretamente.

Aqui está um link para onde estou testando isso:https://regexr.com/3toib

Quero manter o código leve, já que um arquivo médio varia de 3 a 5 GB de tamanho e geralmente há vários (10+) desses arquivos.

PS Redshift é um serviço de banco de dados AWS que usa o Postgre SQL Engine e é capaz de remover aspas de campos devidamente citados e escapar do significado especial de uma cotação com \.

Além disso, estou disposto a fazer isso em Python/qualquer outra linguagem de script, já que o código é leve.

Responder1

Há um problema ENORME com as especificações fornecidas para os dados. Se "|"for uma string válida, ou mais precisamente, uma string entre aspas pode começar com uma barra vertical, então se uma string com uma aspa final ausente, por exemplo "Account1, tiver como primeiro campo entre aspas um que comece com uma barra vertical, por exemplo "|Mary", então não há como determinarem todos os casosse "|for a cotação final |"Account1||||||||||||"|ou a cotação inicial de |"|Mary"|.

Por exemplo, usando uma versão abreviada (para facilitar a leitura) ligeiramente modificada dos dados, onde todas as strings citadas a partir do segundo começam com uma barra vertical e estão faltando as aspas finais

123|110092|ACCT|"HC Account"|"Account1||||||||||||"|Mary|||"|||||132|"|STE|504|1253

pode-se ver que isso será interpretado incorretamente como

123 110092 ACCT "HC Account" "Account1||||||||||||" Mary   "|||||132|" STE 504 1253

Observe que isso é um problema ao usar regexes, Python ou qualquer outra linguagem. O problema do caso geralpodeser "resolvido", mas será complicado e requer o conhecimento de quantos campos existem por linha e a estrutura de dados desses campos. (E sempre pode haver casos extremos deixados de lado.)


Dito isto, uma solução regex que pelo menos detectemaioriacasos de aspas duplas de abertura sem aspas de fechamento requerem uma abordagem de múltiplas passagens, pois a regex precisa capturar todo o texto desde o início de cada linha até a primeira aspa de abertura não processada e sem correspondência. (Caso contrário, como demonstra seu regex, mesmo nos casos mais simples, são encontrados falsos positivos.)

O número de passagens necessárias é o número máximo de campos somente com aspas de abertura para qualquer linha no arquivo inteiro, mais um. O encerramento do processamento de cada arquivo requer a detecção de quando a regex não faz mais modificações no arquivo.

Este é o regex mais simples que funcionará na maioria dos casos:

                    Capturing Group 1           Capturing Group 2
               (All previous valid fields)  (Unclosed opening quote)
  __________________________|_________________________  |
 |                                                    || |
^((?:(?:(?!")[^|\r\n]*|"[^"\r\n]*"(?=$|\|))(?:$|\|))*+)(")
        |____________| |_________________| |______|
              |                 |              |
      Unquoted field  OR  Quoted field     EOL or hypen delimiter

Use-o com esta string de substituição:

$1\\$2

Demonstração

informação relacionada