Separa números, strings de uma linha usando o bash

5

Como separar strings e números de uma linha usando um comando bash.

Exemplo: eu tenho uma string contendo

string123anotherstr456thenanotherstr789

A saída deve ser:

string
123
anotherstr
456
thenanotherstr
789
    
por HUY 08.01.2018 / 15:17

9 respostas

18

GNU grep ou solução compatível:

s="string123anotherstr456thenanotherstr789"
grep -Eo '[[:alpha:]]+|[0-9]+' <<<"$s"
  • [[:alpha:]]+|[0-9]+ - grupo de alternância regex, corresponde a caractere (s) alfabético (s) ou número (s); ambos serão considerados como entradas separadas na saída

A saída:

string
123
anotherstr
456
thenanotherstr
789
    
por 08.01.2018 / 15:31
5

POSIXly:

string=string123anotherstr456thenanotherstr789
sed '
  s/[^[:alnum:]]//g; # remove anything other than letters and numbers
  s/[[:alpha:]]\{1,\}/&\
/g; # insert a newline after each sequence of letters
  s/[0-9]\{1,\}/&\
/g; # same for digits
  s/\n$//; # remove a trailing newline if any' << EOF
$string
EOF
    
por 08.01.2018 / 17:59
4

awk

A entrada contém apenas letras e numerais

Adicione um caractere de nova linha após cada [[:alpha:]]+ (sequência de letras) e após cada [[:digit:]]+ (sequência de numerais):

awk '{ gsub(/([[:alpha:]]+|[[:digit:]]+)/,"&\n",$0) ; printf $0 }' filename

(O & é awk abreviado para a sequência correspondente.)

A entrada contém outros caracteres (por exemplo, pontuação)

Como antes, mas agora também lidando com substrings de caracteres [^[:alnum:]]+ (não-alfabéticos, não numéricos):

awk '{ gsub(/([[:alpha:]]+|[[:digit:]]+|[^[:alnum:]]+)/,"&\n",$0) ; printf $0 }' filename

Números negativos e frações decimais

Trate - (hífen) e . (período) como números:

awk '{ gsub(/([[:alpha:]]+|[[:digit:].-]+|[^[:alnum:].-]+)/,"&\n",$0) ; printf $0 }' filename

Esses caracteres devem aparecer nas expressões [[:digit:].-]+ e [^[:alnum:].-]+ . Além disso, para ser interpretado como um hífen literal, o caractere - deve ser último antes do colchete direito final de cada expressão; caso contrário, indica um intervalo de caracteres.

Exemplo:

[test]$ cat file.txt 
string123another!!str456.001thenanotherstr-789

[test]$ awk '{ gsub(/([[:alpha:]]+|[[:digit:].-]+|[^[:alnum:].-]+)/,"&\n",$0) ; printf $0 }' file.txt 
string
123
another
!!
str
456.001
thenanotherstr
-789

Um exercício para o leitor

Se o arquivo de entrada exigir, você poderá modificar o comando awk para:

  • Certifique-se de que - conte apenas como parte de um número, se ocorrer no início de uma sequência numérica.
  • Permitir números expressos em notação científica.
por 09.01.2018 / 04:15
3
Solução

GNU sed (ou compatível):

s="string123anotherstr456thenanotherstr789"
sed 's/[a-zA-Z]*\|[0-9]*/&\n/g; s/\n$//' <<<"$s"

A saída:

string
123
anotherstr
456
thenanotherstr
789
    
por 08.01.2018 / 15:24
2

Usado abaixo de um liner para conseguir o mesmo. Como testado, funcionou bem

sed "s/[0-9]\{3\}/\n&/g" filename | sed "s/[0-9]\{3\}/&\n/g"| sed '/^$/d'

saída

string
123
anotherstr
456
thenanotherstr
789
    
por 08.01.2018 / 16:43
2

python3

python3 -c '
from itertools import groupby
s = ("".join(g) for k, g in 
    groupby("string123anotherstr456thenanotherstr789", lambda x: x.isalpha()))
print(*s, sep="\n")
'

string
123
anotherstr
456
thenanotherstr
789
    
por 09.01.2018 / 12:08
1

Ainda não vi uma solução Perl, por isso aqui:

$ cat s
string123anotherstr456thenanotherstr789
$ perl -lne 'print $& while /[[:alpha:]]+|[[:digit:]]+/g' < s
string
123
anotherstr
...

É claro que, para definições mais amplas de "números", poderíamos querer usar [-+]?[0-9]+ (sinal inicial), [-+]?[0-9]+(.[0-9]+)? (mais parte fracionária opcional) ou mesmo [-+]?[0-9]+(\.[0-9]+)?([eE][-+]?[0-9]+)? (mais expoente opcional). Os dois últimos requerem pelo menos um dígito antes e depois do ponto decimal, se estiver presente.

    
por 09.01.2018 / 15:02
1

Pure Bash

Isso é relativamente ineficiente porque faz várias cópias (mais curtas) da string original:

declare s=string123anotherstr456thenanotherstr789
while [[ "$s" =~ ^([a-z]+)([0-9]+) ]]; do
  echo ${BASH_REMATCH[1]}
  echo ${BASH_REMATCH[2]}
  s="${s:${#BASH_REMATCH[0]}}"
done

Quantos pares de dígitos de letras por linha você está lidando?

    
por 09.01.2018 / 16:18
1
gawk '{ $1 = $1; print }' FPAT='[a-z]+|[0-9]+' OFS='\n' input.txt

Teste

gawk '{ $1 = $1; print }' FPAT='[a-z]+|[0-9]+' OFS='\n' <<< 'string123anotherstr456thenanotherstr789'

Resultado

string
123
anotherstr
456
thenanotherstr
789
    
por 09.01.2018 / 21:34