Como dividir um “texto de uma linha”, baseado no padrão? [fechadas]

0

Eu tenho um arquivo de 1 linha, contém caracteres não gráficos e quero dividi-lo com base em um padrão. Meu padrão é \[0-9][0-9][0-9] ; como eu posso fazer isso? Por exemplo, quero dividir esta linha:

CHANGE^9^OE@ MORE^0^L^[[00m^OAC DEPOSIT TO WHICH ACCOUNT^N020^^5^L^[(1^[[00m^OAA PAYMENT FROM WHICH ACCOUNT^N020
1abd
CHANGE^
9^OE@ MORE^
0^L^[[00m^OAC DEPOSIT TO WHICH ACCOUNT^N020^^
5^L^[(1^[[00m^OAA PAYMENT FROM WHICH ACCOUNT^N020
2pqr
CHANGE^9^OE@ MORE^0^L^[[00m^OAC DEPOSIT TO WHICH ACCOUNT^N020^^5^L^[(1^[[00m^OAA PAYMENT FROM WHICH ACCOUNT^N020
1abd
CHANGE^
9^OE@ MORE^
0^L^[[00m^OAC DEPOSIT TO WHICH ACCOUNT^N020^^
5^L^[(1^[[00m^OAA PAYMENT FROM WHICH ACCOUNT^N020
2pqr %pre%3xyz
1abd %pre%2pqr %pre%3xyz
3xyz
1abd %pre%2pqr %pre%3xyz

para:

%pre%

Estou usando / bin / sh como shell padrão.

Outro exemplo de entrada:

%pre%

Saída desejada:

%pre%

O tamanho de um arquivo de uma linha é de 80 KB e estou usando a versão 4.2.1 do GNU sed e o SO é o 6.5 do Servidor Red Hat Enterprise Linux (Santiago)

    
por alwaystudent 10.10.2015 / 07:15

3 respostas

2

Houve uma declaração inicial do problema e várias soluções foram propostas. Acontece que eles não funcionaram porque os dados reais não correspondiam à descrição.

Resposta viável

My pattern is: FS[0-9][0-9][0-9], (FS is 'field separator'); how use that in the sed script?

Com Bash e BSD sed , você pode usar isso (que é estreitamente baseado no que John1024 < um href="https://unix.stackexchange.com/a/235176/"> proposto e usa o BSD e o Mac OS X sed notação -E para ativar expressões regulares estendidas):

sed -E $'s/(.)(\x1C[[:digit:]]{3})/\1\\n\2/g' file1

A notação $'…' é o mecanismo ANSI C do Bash. O FS possui valor de byte 28, hex 0x1C ou octal 038. As barras duplas invertidas representam uma barra invertida vista por sed ; o \n precedido por \ satisfaz o manual sed que diz (na seção s/// ):

A line can be split by substituting a newline character into it. To specify a newline character in the replacement string, precede it with a backslash.

Verifique o que funciona com o GNU sed .

Eu também observo que FS é algumas vezes codificado como Control-Backslash (porque Control-A possui código 1, mas A possui código 65 = 64 + 1; barra invertida \ possui código 92 = 64 + 28); isso provavelmente é responsável pela confusão do alwaystudent na questão.

Note que o GNU sed usa -r para fazer o que o BSD faz com% código%; POSIX -E não reconhece a notação.

Histórico de investigação

Recebi o arquivo por e-mail, e se o que recebi for preciso, precisamos de uma caracterização diferente do que é necessário.

Saída de contagem de palavras:

$ wc file1
       1    8804   80106 file1
$

Aqui está a saída de um dump hexadecimal:

$ odx file1 | sed 20q
0x0000: 33 1C 1C 1C 31 31 1C 30 30 31 0E 32 30 31 1C 30   3...11.001.201.0
0x0010: 30 32 0E 32 30 31 1C 30 30 33 0E 32 30 33 1C 30   02.201.003.203.0
0x0020: 30 34 24 20 1C 30 30 35 0E 30 30 32 1C 30 30 36   04$ .005.002.006
0x0030: 0E 30 30 32 1C 30 31 31 0C 1B 28 32 0F 45 40 20   .002.011..(2.E@ 
0x0040: 20 20 59 4F 55 52 20 43 41 52 44 20 49 53 20 4E     YOUR CARD IS N
0x0050: 4F 54 20 20 53 45 52 56 49 43 45 44 0F 46 40 20   OT  SERVICED.F@ 
0x0060: 20 20 20 20 20 20 20 20 20 42 59 20 20 54 48 49            BY  THI
0x0070: 53 20 41 54 4D 20 0F 47 40 20 20 20 20 20 50 4C   S ATM .G@     PL
0x0080: 45 41 53 45 20 54 41 4B 45 20 20 59 4F 55 52 20   EASE TAKE  YOUR 
0x0090: 43 41 52 44 1B 28 37 0F 49 40 20 20 20 20 20 20   CARD.(7.I@      
0x00A0: 20 5C 26 20 2D 28 23 58 3E 3D 20 5C 25 22 40 22    \& -(#X>= \%"@"
0x00B0: 20 41 22 20 0F 4A 40 20 20 20 20 20 20 20 30 57    A" .J@       0W
0x00C0: 5F 40 5B 3F 4A 58 20 2D 28 40 23 51 20 59 5F 22   _@[?JX -(@#Q Y_"
0x00D0: 20 0F 4B 40 20 20 30 3E 5F 40 22 3E 40 26 20 22    .K@  0>_@">@& "
0x00E0: 40 20 3E 5B 3D 20 20 2D 28 40 23 51 20 23 4D 47   @ >[=  -(@#Q #MG
0x00F0: 55 1B 28 32 1C 30 31 34 0C 1B 28 3E 0F 43 40 20   U.(2.014..(>.C@ 
0x0100: 20 20 45 53 50 2D 4C 49 4E 4B 2F 46 54 53 0F 45     ESP-LINK/FTS.E
0x0110: 40 20 20 20 20 20 20 20 41 54 4D 0F 47 40 20 4D   @       ATM.G@ M
0x0120: 41 52 4B 45 54 49 4E 47 20 4D 45 53 53 41 47 45   ARKETING MESSAGE
0x0130: 20 45 32 1C 30 31 35 0C 1C 30 31 38 0C 1C 30 32    E2.015..018..02
$

Aqui estão os mesmos dados de sed :

$ od -c file1 | sed 20q
0000000    3 034 034 034   1   1 034   0   0   1 016   2   0   1 034   0
0000020    0   2 016   2   0   1 034   0   0   3 016   2   0   3 034   0
0000040    0   4   $     034   0   0   5 016   0   0   2 034   0   0   6
0000060  016   0   0   2 034   0   1   1  \f 033   (   2 017   E   @    
0000100            Y   O   U   R       C   A   R   D       I   S       N
0000120    O   T           S   E   R   V   I   C   E   D 017   F   @    
0000140                                        B   Y           T   H   I
0000160    S       A   T   M     017   G   @                       P   L
0000200    E   A   S   E       T   A   K   E           Y   O   U   R    
0000220    C   A   R   D 033   (   7 017   I   @                        
0000240        \   &       -   (   #   X   >   =       \   %   "   @   "
0000260        A   "     017   J   @                               0   W
0000300    _   @   [   ?   J   X       -   (   @   #   Q       Y   _   "
0000320      017   K   @           0   >   _   @   "   >   @   &       "
0000340    @       >   [   =           -   (   @   #   Q       #   M   G
0000360    U 033   (   2 034   0   1   4  \f 033   (   > 017   C   @    
0000400            E   S   P   -   L   I   N   K   /   F   T   S 017   E
0000420    @                               A   T   M 017   G   @       M
0000440    A   R   K   E   T   I   N   G       M   E   S   S   A   G   E
0000460        E   2 034   0   1   5  \f 034   0   1   8  \f 034   0   2
$

E aqui está uma análise de frequência de caracteres dos dados:

  =   3:      1
  =  10:      1
  =  12:    648
  =  14:    883
  =  15:   3461
  =  27:   1384
  =  28:    722
  =  32:  15248
! =  33:    178
" =  34:   1236
# =  35:   1847
$ =  36:      2
% =  37:     44
& =  38:    945
' =  39:    197
( =  40:   2096
) =  41:   1434
* =  42:    695
+ =  43:     25
, =  44:    446
- =  45:   1431
. =  46:     92
/ =  47:     29
0 =  48:   2453
1 =  49:   1279
2 =  50:   1052
3 =  51:    758
4 =  52:    427
5 =  53:    565
6 =  54:    299
7 =  55:    862
8 =  56:    414
9 =  57:    423
: =  58:     78
; =  59:    330
< =  60:      3
= =  61:    313
> =  62:   1683
? =  63:     60
@ =  64:   3472
A =  65:   2268
B =  66:    791
C =  67:   2034
D =  68:   1480
E =  69:   2862
F =  70:    732
G =  71:    692
H =  72:    799
I =  73:   1202
J =  74:    360
K =  75:    358
L =  76:    963
M =  77:    823
N =  78:   1483
O =  79:   1726
P =  80:    588
Q =  81:    507
R =  82:   1411
S =  83:   1624
T =  84:   1905
U =  85:   1172
V =  86:    151
W =  87:    372
X =  88:   1063
Y =  89:    647
Z =  90:    758
[ =  91:   1026
\ =  92:    665
] =  93:    275
^ =  94:    397
_ =  95:   1179
a =  97:      1
c =  99:      1
d = 100:      1
m = 109:    240
o = 111:      2
p = 112:      2
q = 113:      4
r = 114:      2
s = 115:      2
t = 116:      4
u = 117:      1
w = 119:      1
y = 121:      1
z = 122:     15

A soma dos números na última coluna é 80106, que concorda com od -c .

Como você pode ver, há apenas uma nova linha (código 10) e aparece no final do arquivo. Há muito poucas letras minúsculas, muitas letras maiúsculas, um número moderado de barras invertidas, mas (o que você não pode ver pelos dados mostrados até agora é isso) nenhuma das barras invertidas é seguida por um dígito. Observe que não há códigos de caracteres fora do intervalo ASCII (nenhum com o bit alto definido) e a cobertura do intervalo ASCII também não está completa.

Eu escrevi um programa de análise rápida para ver quais caracteres seguem uma barra invertida:

#include <stdio.h>

int main(void)
{
    int c;
    int count[256] = { 0 };
    while ((c = getchar()) != EOF)
    {
        if (c == '\')
        {
            c = getchar();
            count[c]++;
        }
    }

    for (int i = 0; i < 255; i++)
    {
        if (count[i] != 0)
            printf("%3d = %5d\n", i, count[i]);
    }

    return 0;
}

Quando executado no arquivo, é produzido:

 12 =     3
 14 =    58
 15 =     3
 27 =    25
 34 =    10
 35 =    51
 37 =    14
 38 =   126
 40 =     9
 44 =    51
 45 =    26
 47 =     2
 59 =    17
 62 =   118
 64 =    46
 65 =     2
 66 =     2
 67 =    17
 69 =     1
 71 =     4
 72 =     5
 74 =    15
 79 =     1
 81 =     9
 83 =     1
 85 =     5
 88 =    18
 90 =    12
 91 =     6
 95 =     8

A soma das contagens é de 665, o que concorda com o número de barras invertidas na análise original do personagem.

Os dígitos têm códigos 48..57; nenhum dos caracteres depois de uma barra invertida é um dígito.

É por isso que as várias soluções mostradas falham - elas nunca tiveram uma chance, porque os dados nem sequer começam a se parecer com o que é descrito.

    
por 11.10.2015 / 09:13
2

Usando sed:

$ cat file
$ cat file2
CHANGE^9^OE@ MORE^0^L^[[00m^OAC DEPOSIT TO WHICH ACCOUNT^N020^^5^L^[(1^[[00m^OAA PAYMENT FROM WHICH ACCOUNT^N020
$ sed -E 's/(.)(\[[:digit:]]{3})/\n/g' file2
CHANGE^
9^OE@ MORE^
0^L^[[00m^OAC DEPOSIT TO WHICH ACCOUNT^N020^^
5^L^[(1^[[00m^OAA PAYMENT FROM WHICH ACCOUNT^N020
1abd
sed -E 's/(.)(\x1c[[:digit:]]{3})/\n/g'
2pqr
$ echo $'One\x1c123Two\x1c456Three\x1c7none' >newfile
3xyz $ sed -E 's/(.)(\[[:digit:]]{3})/\n/g' file
$ sed -E 's/(.)(\x1c[[:digit:]]{3})/\n/g' newfile
One
123Two
456Three7none
1abd
One^3Two^6Three^none
2pqr
CHANGE^9^OE@ MORE^0^L^[[00m^OAC DEPOSIT TO WHICH ACCOUNT^N020^^5^L^[(1^[[00m^OAA PAYMENT FROM WHICH ACCOUNT^N020
3xyz

Usando o mesmo comando sed, mas seu outro exemplo de dados:

$ cat file
$ cat file2
CHANGE^9^OE@ MORE^0^L^[[00m^OAC DEPOSIT TO WHICH ACCOUNT^N020^^5^L^[(1^[[00m^OAA PAYMENT FROM WHICH ACCOUNT^N020
$ sed -E 's/(.)(\[[:digit:]]{3})/\n/g' file2
CHANGE^
9^OE@ MORE^
0^L^[[00m^OAC DEPOSIT TO WHICH ACCOUNT^N020^^
5^L^[(1^[[00m^OAA PAYMENT FROM WHICH ACCOUNT^N020
1abd
sed -E 's/(.)(\x1c[[:digit:]]{3})/\n/g'
2pqr
$ echo $'One\x1c123Two\x1c456Three\x1c7none' >newfile
3xyz $ sed -E 's/(.)(\[[:digit:]]{3})/\n/g' file
$ sed -E 's/(.)(\x1c[[:digit:]]{3})/\n/g' newfile
One
123Two
456Three7none
1abd
One^3Two^6Three^none
2pqr
CHANGE^9^OE@ MORE^0^L^[[00m^OAC DEPOSIT TO WHICH ACCOUNT^N020^^5^L^[(1^[[00m^OAA PAYMENT FROM WHICH ACCOUNT^N020
3xyz

Atualização: divisão no dígito-dígito-dígito

O caractere "separador de arquivos" (FS) ASCII é hexadecimal 1C . Usando o GNU sed:

%pre%

Para demonstrar isso, vamos criar um arquivo de teste:

%pre%

Agora, vamos executar sed :

%pre%

As linhas são divididas com sucesso.

Discussão

No meu terminal, como mostrado acima, os caracteres do FS são invisíveis. O tornar-se visível ao executar menos. Por exemplo, a execução de less newfile resulta na exibição:

%pre%

Aqui podemos ver que o caractere FS é exibido como ^\ . Isso é consistente com a segunda entrada de exemplo mostrada na pergunta:

%pre%     
por 10.10.2015 / 09:04
-1

Se você quiser ir de:

tr \  \n <in >out
1abc
sed 's/\[0-9]\{3\}/&\n/g' <in >out
2jkl
sed 's/\[0-9]\{3\}/\n&/g' <in >out
1 abc

{   tr '\' \n | 
    sed -e:t \
        -e'$!N;/\n[0-9]\{3\}/!s/\n/\/;tt' \
        -e's/\n/&\/;P;D'
}   <infile >outfile
1abc
CHANGE^
9^OE@ MORE^
0^L^[[00m^OAC DEPOSIT TO WHICH ACCOUNT^N020^^
5^L^[(1^[[00m^OAA PAYMENT FROM WHICH ACCOUNT^N020
2jkl
tr \  \n <in >out
1abc
sed 's/\[0-9]\{3\}/&\n/g' <in >out
2jkl
sed 's/\[0-9]\{3\}/\n&/g' <in >out
1 abc

{   tr '\' \n | 
    sed -e:t \
        -e'$!N;/\n[0-9]\{3\}/!s/\n/\/;tt' \
        -e's/\n/&\/;P;D'
}   <infile >outfile
1abc
CHANGE^
9^OE@ MORE^
0^L^[[00m^OAC DEPOSIT TO WHICH ACCOUNT^N020^^
5^L^[(1^[[00m^OAA PAYMENT FROM WHICH ACCOUNT^N020
2jkl %pre%3xyz
2 jkl %pre%3 xyz
3xyz
1abc %pre%2jkl %pre%3xyz
3xyz
2 jkl %pre%3 xyz
3xyz
1abc %pre%2jkl %pre%3xyz

... para ...

%pre%

... então a solução mais simples e eficaz é:

%pre%

Isso, no entanto, não é o mesmo que dividir sua entrada em \n ewlines de acordo com seu padrão \[0-9][0-9][0-9] , que pode ser afetado como:

%pre%

... e resultaria em ...

%pre%

... embora suponha que você queira inserir um \n ewline antes de cada ocorrência de \[0-9][0-9][0-9] , caso em que você poderia fazer:

%pre%

... para obter ...

%pre%

... com um espaço à direita no final de cada linha.

Mas ambos podem ter problemas em lidar com linhas de entrada muito longas. Se todo o seu arquivo de entrada é apenas uma linha, então podemos fazer de forma confiável:

%pre%

A cadeia de comando acima irá tr ansliterar todas barras invertidas na entrada para \n ewline caracteres, em seguida, canalizar os resultados para sed que recursivamente t est a cabeça de cada linha de entrada para três dígitos. tr não terá nenhum problema em lidar com linhas de entrada longas e, quando acabar, a saída deverá conter pelo menos muitos caracteres \n ewline conforme necessário. Se um caractere \n ewline for não seguido imediatamente por três dígitos, ele será simplesmente substituído por uma barra invertida, caso contrário, é , então uma barra invertida será inserida entre \n ewline character e os três dígitos.

Os resultados para o seu segundo exemplo são:

%pre%     
por 10.10.2015 / 19:56