extrair cada enésimo caractere de uma string

extrair cada enésimo caractere de uma string

Estou tentando descobrir uma solução paraessepergunta. Minha abordagem para esse problema até agora é a seguinte.

  • Anexe todos os caracteres para formar uma string longa.
  • Após a etapa acima, remova todos os espaços em branco ou tabulações para que tenhamos apenas uma string grande.

Consegui estabelecer as etapas acima com o comando abaixo.

column -s '\t' inputfile | tr -d '[:space:]'

Então, para um arquivo de entrada como este,

1   0   0   0   0   0

0   1   1   1   0   0

Depois de aplicar o comando acima, tenho os valores como,

100000011100

Agora, nesta grande string, estou tentando aplicar uma abordagem conforme abaixo.

Extraia cada sexto caractere (conforme desejado pelo OP original) e anexe-o a um elemento da matriz até o final da string.

Então, basicamente, com a etapa acima, estou tentando criar os elementos do array como,

10(1º ecaracteres ), 01(2º ecaracteres ), 01(3º ecaracteres ), 01(4º e 10º caracteres ), 00(5º e 11º caracteres ), 00(6º e 12º caracteres). o personagem).

Então, minha pergunta é: como eu poderia extrair cada enésimo caractere para poder adicioná-los a uma matriz para prosseguir? (n=6, neste caso).

Responder1

Duas linhas

Aqui está uma bashsolução pura que produz um basharray:

s="100000011100"
array=($(
    for ((i=0; i<${#s}-6; i++))
    do
        echo "${s:$i:1}${s:$((i+6)):1}"
    done
    ))
echo "${array[@]}"

Isso produz a mesma saída mostrada na pergunta:

10 01 01 01 00 00

O elemento chave aqui é o uso do bashexpansão de substring. Bash permite a extração de substrings de uma variável, digamos parameter, via ${parameter:offset:length}. No nosso caso, o deslocamento é determinado pela variável do loop ie o comprimento é sempre 1.

Solução geral para qualquer número de linhas

Suponha, por exemplo, que nossa string original tenha 18 caracteres e queremos extrair o i-ésimo, o i+6-ésimo e o i+12-ésimo caracteres para i de 0 a 5. Então:

s="100000011100234567"
array=($(
    for ((i=0; i<6; i++))
    do
        new=${s:$i:1}
        for ((j=i+6; j<${#s}; j=j+6))
        do 
            new="$new${s:$j:1}"
        done
        echo "$new"
    done
    ))

echo "${array[@]}"

Isso produz a saída:

102 013 014 015 006 007

Este mesmo código se estende a um número arbitrário de linhas de 6 caracteres. Por exemplo, se stiver três linhas (18 caracteres):

s="100000011100234567abcdef"

Então, a saída se torna:

102a 013b 014c 015d 006e 007f

Responder2

Usando perl:

$ echo 100000011100 | perl -nle '
    for ($i = 0; $i < length()/2; $i++) {
        print substr($_,$i,1), substr($_,$i+6,1);
    }
'
10
01
01
01
00
00

Funciona para duas linhas. Se você quiser trabalhar com linhas arbitrárias, você deve processar as linhas diretamente, em vez de construir uma string grande. Com esta entrada:

1   0   0   0   0   0                                                           
0   1   1   1   0   0                                                           
0   0   0   0   0   0

Tentar:

$ perl -anle '
    for ($i = 0; $i <= $#F; $i++) {
      push @{$h{$i}}, $F[$i];
    }
    END {
        print @{$h{$_}} for keys %h;
    }
' file
000
010
000
100
010
010

Responder3

Como solução shell, getoptsé provavelmente mais fácil. O problema getoptsé que ele é especificado pelo POSIX para fazer exatamente o que você está pedindo - processar um fluxo de bytes em um loop de shell. Eu sei que isso parece estranho, porque, se você é como eu antes de aprender isso, provavelmente está pensando:bem, caramba, pensei que era para lidar com opções de linha de comando.O que é verdade, mas a primeira coisa também é. Considerar:

-thisisonelongstringconsistingofseparatecommandlineswitches

Sim, getoptstem que lidar com isso. Ele tem que dividir char por char em um loop e retornar para você cada caractere na variável shell $OPTARGou em outra que você especifica por nome, tudo dependendo de quão específico você obtém ao chamá-lo. Além do mais, ele deve retornar erros nas variáveis ​​do shell esalve seu progressoquando isso acontece na variável shell $OPTINDpara que possaretomar exatamente de onde parouse você puder de alguma forma resolver isso. E tem que fazer todo o trabalho sem invocar um único subshell.

Então digamos que temos:

arg=$(seq -s '' 1000); set --
while getopts :0123456789 v -"${arg}"
do [ "$((i=$i+1<6?$i+1:0))" -gt 0 ] ||
set "$@" "$v"
done

Hmmm.... Será que funcionou?

echo "$((${#arg}/6))" "$#"
482 482

Muito legal...

eval '
printf %.1s\\n "${arg#'"$(printf %0$((124*6-1))d | tr 0 \?)"'}" "${124}"'
4
4

Então, como você pode ver, o getoptscomando define completamente o array para cada sexto byte da string. E não precisa ser números como este - nem deve ser caracteres seguros para shell - e você nem precisa especificar os caracteres de destino como fiz acima 01234565789. Eu testei isso repetidamente em vários shells e todos eles simplesmente funcionam. Existem algumas peculiaridades - bashdescartará o primeiro caractere se for um caractere de espaço em branco - dashaceita os :dois pontos como um parâmetro especificado, mesmo que seja o único que o POSIX proíbe especificamente. Mas nada disso importa porque getoptsainda deposita o valor do opt char atual $OPTARGmesmo quando ele retorna um erro(representado por um ? atribuído à sua opt var especificada)e de outra forma explicitamente desativada, $OPTARGa menos que você tenha declarado que uma opção deve ter um argumento. E a coisa do espaço em branco é uma coisa boa - apenas descarta umprincipalespaço, o que é excelente, pois, ao trabalhar com valores desconhecidos, você pode fazer:

getopts : o -" $unknown_value"

... para iniciar o loop sem qualquer perigo de o primeiro caractere realmente estar na sua string de argumentos aceita - o que resultaria em getoptsinserir tudo $OPTARGde uma vez - como um argumento.

Aqui está outro exemplo:

OPTIND=1
while getopts : o -" $(dd if=/dev/urandom bs=16 count=1 2>/dev/null)"                         
do printf '\\%04o' "'$OPTARG"; done  

\0040\0150\0071\0365\0320\0070\0161\0064\0274\0115\0012\0215\0222\0271\0146\0057\0166

Eu configurei $OPTIND=1na primeira linha porque acabei de usar getoptse, até que você reinicie, ele espera que sua próxima chamada continue de onde parou - "${arg2}"em outras palavras, ele quer. Mas não estou com vontade de dar e estou fazendo uma coisa diferente agora, então eu informo redefinindo $OPTINDem que ponto está pronto para ir.

Neste eu usei zsh- o que não questiona o espaço inicial - e então o primeiro caractere é octal 40 - o caractere de espaço. Mas eu não costumo usar getoptsdessa maneira - geralmente uso paraevitarfazendo um write()para cada byte e, em vez disso, atribua sua saída - que vem em uma variável - a outra variável de shell - como fiz acima de setcerta forma. Então, quando estiver pronto, posso pegar a string inteira e, quando o faço, geralmente removo o primeiro byte.

Responder4

sedé a primeira coisa que me vem à mente.

$ echo 1234567890abcdefghijklmnopqrstuvwxyz | sed 's/.\{5\}\(.\)/\1/g'
6bhntz

Combine 5 personagens, capture o 6º e substitua todos pelo personagem capturado.

No entanto, isso terá um problema se o comprimento da string não for um múltiplo exato de 6:

$ echo 1234567890abcdefghijklmnopqrstuvwxy | sed 's/.\{5\}\(.\)/\1/g' 
6bhntuvwxy

Mas podemos consertar isso alterando sedum pouco:

$ echo 1234567890abcdefghijklmnopqrstuvwxy | sed 's/.\{1,5\}\(.\{0,1\}\)/\1/g'
6bhnt

Devido à natureza gananciosa do regex, as correspondências de comprimento variável corresponderão o máximo que puderem e, se não sobrar nada para a captura, ele não será capturado e os caracteres serão simplesmente excluídos.

informação relacionada