如何根据模式分割“单行文本”?

如何根据模式分割“单行文本”?

我有一个 1 行文件,包含非图形字符,我想根据模式拆分它。我的模式是 \[0-9][0-9][0-9];我该怎么做?例如,我想分割这一行:

\001abd \002pqr \003xyz

到:

\001abd 
\002pqr 
\003xyz

我使用 /bin/sh 作为默认 shell。

另一个输入示例:

CHANGE^\039^OE@ MORE^\040^L^[[00m^OAC DEPOSIT TO WHICH ACCOUNT^N020^^\055^L^[(1^[[00m^OAA PAYMENT FROM WHICH ACCOUNT^N020

期望的输出:

CHANGE^
\039^OE@ MORE^
\040^L^[[00m^OAC DEPOSIT TO WHICH ACCOUNT^N020^^
\055^L^[(1^[[00m^OAA PAYMENT FROM WHICH ACCOUNT^N020

一行文件的大小为 80KB,我使用的是 GNU sed 版本 4.2.1,操作系统是 Red Hat Enterprise Linux Server 版本 6.5(圣地亚哥)

答案1

有一个初步的问题陈述,并提出了各种解决方案。事实证明这些不起作用,因为实际数据与描述不符。

可行的答案

我的模式是:FS[0-9][0-9][0-9], (FS 是“字段分隔符”);如何在脚本中使用它sed

对于 Bash 和 BSD sed,您可以使用它(这与约翰1024 建议的并使用 BSD 和 Mac OS Xsed-E启用扩展正则表达式的符号):

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

符号$'…'是 Bash 的ANSI C 引用机制。 FS 的字节值是 28,十六进制 0x1C 或八进制 038。双反斜杠表示sed;看到的反斜杠。\n前面的内容\\满足sed手册中的规定(在本s///节中):

可以通过将换行符替换到其中来分割行。要在替换字符串中指定换行符,请在其前面添加反斜杠。

检查什么适用于 GNU sed

我还观察到 FS 有时被编码为Control-Backslash(因为Control-A有代码 1,但A有代码 65 = 64 + 1;反斜杠\有代码 92 = 64 + 28);这可能解释了永远是学生问题中的混乱。

请注意,GNUsed用于-r执行 BSD 所做的操作-E; POSIXsed不识别任何一种符号。

研究背景

我已通过电子邮件发送了该文件,如果我收到的内容是准确的,那么我们需要对所需内容进行不同的描述。

字数统计输出:

$ wc file1
       1    8804   80106 file1
$

这是十六进制转储的输出:

$ 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
$

以下是来自 的相同数据od -c

$ 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
$

这是数据的字符频率分析:

  =   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

最后一列中的数字之和为 80106,与 一致wc

正如您所看到的,只有一个换行符(代码 10),并且它出现在文件的最后。小写字母很少,大写字母很多,反斜杠数量适中,但是(从目前显示的数据中看不到的是)反斜杠后面没有一个数字。请注意,不存在 ASCII 范围之外的字符代码(没有设置高位的字符代码),并且 ASCII 范围的覆盖也不完整。

我编写了一个快速分析程序来查看反斜杠后面有哪些字符:

#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;
}

当运行该文件时,它产生:

 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

计数总和为 665,与原始字符分析中的反斜杠数量一致。

数字的代码为 48..57;反斜杠后面的字符都不是数字。

这就是为什么所展示的各种解决方案都失败了——它们从来没有机会,因为数据甚至开始与描述的内容不相似。

答案2

使用 sed:

$ cat file
\001abd \002pqr \003xyz
$ sed -E 's/(.)(\\[[:digit:]]{3})/\1\n\2/g' file
\001abd 
\002pqr 
\003xyz

使用相同的 sed 命令但使用其他数据示例:

$ cat file2
CHANGE^\039^OE@ MORE^\040^L^[[00m^OAC DEPOSIT TO WHICH ACCOUNT^N020^^\055^L^[(1^[[00m^OAA PAYMENT FROM WHICH ACCOUNT^N020
$ sed -E 's/(.)(\\[[:digit:]]{3})/\1\n\2/g' file2
CHANGE^
\039^OE@ MORE^
\040^L^[[00m^OAC DEPOSIT TO WHICH ACCOUNT^N020^^
\055^L^[(1^[[00m^OAA PAYMENT FROM WHICH ACCOUNT^N020

更新:FS-数字-数字-数字的拆分

ASCII“文件分隔符”(FS) 字符是十六进制的1C。使用 GNU sed:

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

为了演示这一点,让我们创建一个测试文件:

$ echo $'One\x1c123Two\x1c456Three\x1c7none' >newfile

现在,让我们运行sed

$ sed -E 's/(.)(\x1c[[:digit:]]{3})/\1\n\2/g' newfile
One
123Two
456Three7none

线路已成功分割。

讨论

在我的终端上,如上所示,FS 字符是不可见的。当跑得少时,它们就会变得可见。例如,运行less newfile结果显示:

One^\123Two^\456Three^\7none

这里我们可以看到FS字符显示为^\。这与问题中显示的第二个示例输入一致:

CHANGE^\039^OE@ MORE^\040^L^[[00m^OAC DEPOSIT TO WHICH ACCOUNT^N020^^\055^L^[(1^[[00m^OAA PAYMENT FROM WHICH ACCOUNT^N020

答案3

如果你想从:

\001abc \002jkl \003xyz

...到...

\001abc
\002jkl
\003xyz

...那么最简单、最高效的解决方案是:

tr \  \\n <in >out

\n然而,这与根据您的模式在 ewlines 上拆分您的输入不同\[0-9][0-9][0-9],这可能会受到如下影响:

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

...并且会导致...

\001
abc \002
jkl \003
xyz

...虽然我想你的意思可能是你想插入一条\newline每次出现\[0-9][0-9][0-9],在这种情况下你可以这样做:

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

...要得到...

\001abc
\002jkl
\003xyz

...每行末尾有一个尾随空格。

但是这两种方法在处理很长的输入行时都可能存在问题。如果您的整个输入文件只有一行,那么我们可以可靠地执行以下操作:

{   tr '\\' \\n | 
    sed -e:t \
        -e'$!N;/\n[0-9]\{3\}/!s/\n/\\/;tt' \
        -e's/\n/&\\/;P;D'
}   <infile >outfile

上面的命令链将被tr解析全部输入中的反斜杠为\newline 字符,然后将结果通过管道传输到该结果,sed该结果将递归地t估计每个输入行的头部三个数字。tr处理长输入行根本不会有任何问题,并且当它完成时,它的输出应该至少包含\n您需要的许多 ewline 字符。如果\newline 字符是不是紧随其后的是三位数字,然后简单地用反斜杠替换,否则如果\n然后在ewline 字符和三个数字之间插入一个反斜杠。

第二个示例的结果是:


CHANGE^
\039^OE@ MORE^
\040^L^[[00m^OAC DEPOSIT TO WHICH ACCOUNT^N020^^
\055^L^[(1^[[00m^OAA PAYMENT FROM WHICH ACCOUNT^N020

相关内容