extraer cada enésimo carácter de una cadena

extraer cada enésimo carácter de una cadena

Estoy tratando de encontrar una solución paraestepregunta. Mi enfoque a este problema hasta ahora es el siguiente.

  • Agregue todos los caracteres juntos para formar una cadena larga.
  • Después del paso anterior, elimine todos los espacios en blanco o espacios de tabulación para que solo tengamos una cadena grande.

Pude establecer los pasos anteriores con el siguiente comando.

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

Entonces, para un archivo de entrada como este,

1   0   0   0   0   0

0   1   1   1   0   0

Después de aplicar el comando anterior tengo los valores como,

100000011100

Ahora, en esta gran cadena, estoy intentando aplicar el enfoque que se muestra a continuación.

Extraiga cada sexto carácter (como quiere el OP original) y agréguelo a un elemento de matriz hasta el final de la cadena.

Básicamente, con el paso anterior, estoy tratando de crear los elementos de la matriz como,

10(1.º y 7.º carácter ), 01(2.º y 8.º carácter ), 01(3.º y 9.º carácter ), 01(4.º y 10.º carácter ), 00( 5.º y 11.º carácter ), 00(6.º y 12.º carácter), º personaje) .

Entonces mi pregunta es, ¿cómo podría extraer cada enésimo carácter para poder agregarlos a una matriz y continuar? (n=6, en este caso).

Respuesta1

Dos lineas

Aquí hay una bashsolución pura que produce una bashmatriz:

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

Esto produce el mismo resultado que se muestra en la pregunta:

10 01 01 01 00 00

El elemento clave aquí es el uso de bash.expansión de subcadena. Bash permite extraer subcadenas de una variable, por ejemplo parameter, a través de ${parameter:offset:length}. En nuestro caso, el desplazamiento está determinado por la variable del bucle iy la longitud es siempre 1.

Solución general para cualquier número de líneas

Supongamos, por ejemplo, que nuestra cadena original tiene 18 caracteres y queremos extraer los caracteres i-ésimo, i+6-ésimo y i+12-ésimo para i de 0 a 5. Entonces:

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[@]}"

Esto produce la salida:

102 013 014 015 006 007

Este mismo código se extiende a un número arbitrario de líneas de 6 caracteres. Por ejemplo, si stiene tres líneas (18 caracteres):

s="100000011100234567abcdef"

Entonces, la salida se convierte en:

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

Respuesta2

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 dos líneas. Si desea trabajar con líneas arbitrarias, debe procesar las líneas directamente, en lugar de crear cadenas grandes. Con esta entrada:

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

Intentar:

$ 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

Respuesta3

Como solución shell, getoptsprobablemente sea la más sencilla. Lo que pasa getoptses que está especificado por POSIX para hacer exactamente lo que estás pidiendo: procesar un flujo de bytes en un bucle de shell. Sé que suena raro porque, si eres como yo antes de enterarme de esto, probablemente estés pensando:Bueno, vaya, pensé que se suponía que debía manejar cambios de línea de comandos.Lo cual es cierto, pero también lo es lo primero. Considerar:

-thisisonelongstringconsistingofseparatecommandlineswitches

Sí, getoptstiene que manejar eso. Tiene que dividir ese carácter por carácter en un bucle y devolverle cada carácter en la variable de shell $OPTARGo en otra que especifique por nombre, todo dependiendo de qué tan específico sea cuando lo llame. Es más, tiene que devolver errores en las variables del shell yguardar su progresocuando lo hace en la variable de shell $OPTINDpara que puedareanudar justo donde lo dejósi de alguna manera puedes solucionarlo. Y tiene que hacer todo el trabajo sin invocar una sola subcapa.

Entonces digamos que tenemos:

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

Hmmm.... Me pregunto si funcionó.

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

Qué lindo...

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

Entonces, como puede ver, el getoptscomando configura completamente la matriz para cada sexto byte de la cadena. Y no tienen que ser números como este, ni siquiera deben ser caracteres seguros para el shell, y ni siquiera es necesario especificar los caracteres de destino como lo hice anteriormente con 01234565789ninguno de los dos. He probado esto repetidamente en muchos shells y todos simplemente funcionan. Hay algunas peculiaridades: bashdescartará el primer carácter si es un espacio en blanco; dashacepta los :dos puntos como parámetro especificado a pesar de que es el único POSIX que lo prohíbe específicamente. Pero nada de eso importa porque getoptsaún deposita el valor del carácter de opción actual $OPTARGincluso cuando le devuelve un error.(representado por un? asignado a su var de opción especificada)y de lo contrario se desarma explícitamente $OPTARGa menos que haya declarado que una opción debería tener un argumento. Y lo de los espacios en blanco es algo bueno: sólo descarta unaprincipalespacio, lo cual es excelente, porque, cuando se trabaja con valores desconocidos, puedes hacer:

getopts : o -" $unknown_value"

... para iniciar el ciclo sin ningún peligro de que el primer carácter esté realmente en la cadena de argumentos aceptada, lo que daría como resultado getoptsincluir todo $OPTARGa la vez, como argumento.

Aquí hay otro ejemplo:

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

Lo configuré $OPTIND=1en la primera línea porque acabo de usar getoptsy, hasta que lo restablezca, espera que su próxima llamada continúe donde lo dejó; "${arg2}"en otras palabras, quiere. Pero no tengo ganas de dar y ahora estoy haciendo algo diferente, así que le hago saber reiniciando $OPTINDen qué punto está listo.

En este usé zsh, que no tiene objeciones sobre un espacio inicial, por lo que el primer carácter es octal 40, el carácter de espacio. Aunque normalmente no lo uso getoptsde esa manera; normalmente lo uso paraevitarhaciendo a write()para cada byte y en su lugar asigna su salida, que viene en una variable, a otra variable de shell, como lo hice anteriormente en setcierto modo. Luego, cuando esté listo, puedo tomar toda la cadena y, cuando lo hago, suelo eliminar el primer byte.

Respuesta4

sedes lo primero que me viene a la mente.

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

Combina 5 personajes, captura el sexto y reemplázalos todos con ese personaje capturado.

Sin embargo, esto tendrá un problema si la longitud de la cadena no es un múltiplo exacto de 6:

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

Pero podemos solucionar esto modificando un sedpoco:

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

Debido a la naturaleza codiciosa de las expresiones regulares, las coincidencias de longitud variable coincidirán tanto como puedan, y si no queda nada para la captura, entonces no se captura y los caracteres simplemente se eliminan.

información relacionada