문자열에서 모든 n번째 문자를 추출합니다.

문자열에서 모든 n번째 문자를 추출합니다.

나는 이에 대한 해결책을 찾으려고 노력하고 있습니다.이것질문. 지금까지 이 문제에 대한 나의 접근 방식은 다음과 같습니다.

  • 모든 문자를 함께 추가하여 긴 문자열로 만듭니다.
  • 위 단계 후에 공백이나 탭 공백을 모두 제거하면 하나의 큰 문자열만 남게 됩니다.

아래 명령을 사용하여 위 단계를 설정할 수 있었습니다.

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

따라서 이와 같은 입력 파일의 경우

1   0   0   0   0   0

0   1   1   1   0   0

위 명령을 적용한 후 다음과 같은 값을 갖게 됩니다.

100000011100

이제 이 큰 문자열에서 아래와 같은 접근 방식을 적용하려고 합니다.

원래 OP가 원하는 대로 모든 6 번째 문자를 추출하고 문자열 끝까지 배열 요소에 추가합니다.

따라서 기본적으로 위 단계를 통해 배열 요소를 다음과 같이 생성하려고 합니다.

10(1 번째 와 7 번째 문자), 01(2 번째 와 8 번째 문자), 01(3 번째 와 9 번째 문자), 01(4 번째 와 10 번째 문자), 00(5 번째 와 11 번째 문자), 00(6 번째 와 12번째 문자) 번째 문자) .

그래서 내 질문은, 더 진행하기 위해 배열에 추가할 수 있도록 n 번째 문자마다 어떻게 추출할 수 있습니까? (이 경우 n=6).

답변1

두 줄

bash다음은 배열 을 생성하는 순수한 솔루션입니다 bash.

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

그러면 질문에 표시된 것과 동일한 출력이 생성됩니다.

10 01 01 01 00 00

여기서 핵심 요소는 bash를 사용하는 것입니다.하위 문자열 확장. Bash를 사용하면 를 parameter통해 변수에서 하위 문자열을 추출할 수 있습니다 ${parameter:offset:length}. 우리의 경우 오프셋은 루프 변수에 의해 결정되며 i길이는 항상 입니다 1.

모든 라인 수에 대한 일반적인 솔루션

예를 들어 원래 문자열에 18개의 문자가 있고 i에 대한 i번째, i+6번째 및 i+12번째 문자를 0에서 5까지 추출하려고 한다고 가정합니다. 그런 다음:

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

그러면 다음과 같은 출력이 생성됩니다.

102 013 014 015 006 007

이 동일한 코드는 임의 개수의 6자 라인으로 확장됩니다. 예를 들어 s세 줄(18자)이 있는 경우:

s="100000011100234567abcdef"

그러면 출력은 다음과 같습니다.

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

답변2

사용 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

두 줄로 작동합니다. 임의의 라인으로 작업하려면 큰 문자열을 작성하는 대신 라인을 직접 처리해야 합니다. 이 입력을 사용하면 다음과 같습니다.

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

노력하다:

$ 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

답변3

쉘 솔루션으로는 getopts아마도 가장 쉬울 것입니다. 문제는 getopts사용자가 요구하는 작업을 정확하게 수행하도록 POSIX에 지정되어 있다는 것입니다. 즉, 쉘 루프에서 바이트 스트림을 처리합니다. 이상하게 들린다는 걸 알아요. 제가 이 말을 배우기 전의 여러분도 저와 같다면 아마도 이렇게 생각했을 겁니다.글쎄요, 저는 그것이 명령줄 스위치를 처리해야 한다고 생각했습니다.그것은 사실이지만, 첫 번째도 마찬가지입니다. 고려하다:

-thisisonelongstringconsistingofseparatecommandlineswitches

예, getopts처리해야 합니다. 루프에서 해당 문자를 문자별로 분할하고 $OPTARG호출할 때 얻는 구체적인 정도에 따라 쉘 변수 또는 이름으로 지정한 다른 변수의 각 문자를 반환해야 합니다. 게다가 쉘 변수에서 오류를 반환해야 하고진행 상황을 저장하다쉘 변수에서 그렇게 할 $OPTIND중단된 부분부터 다시 시작어떻게든 해결할 수 있다면. 그리고 단일 서브셸을 호출하지 않고 전체 작업을 수행해야 합니다.

그럼 우리가 가지고 있다고 가정 해 봅시다 :

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

흠.... 효과가 있었는지 궁금하네요?

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

그거 좋은데...

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

따라서 보시다시피 이 getopts명령은 문자열의 6번째 바이트마다 배열을 완전히 설정합니다. 그리고 이와 같은 숫자일 필요는 없으며 쉘 안전 문자일 ​​필요도 없습니다. 위에서 했던 것처럼 대상 문자를 지정할 필요도 없습니다 01234565789. 나는 이것을 많은 쉘에서 반복적으로 테스트했으며 모두 작동합니다. 몇 가지 특이한 점이 있습니다. 공백 bash문자인 경우 첫 번째 문자를 버립니다. 콜론이 특별히 금지된 유일한 POSIX임에도 불구하고 콜론을 지정된 매개변수로 dash허용합니다 . 그러나 오류가 반환되는 경우에도 현재 opt char의 값을 계속 저장하기 :때문에 그 중 아무 것도 중요하지 않습니다.getopts$OPTARG(지정된 opt var에 할당된 ?로 표시됨)$OPTARG옵션에 인수가 있어야 한다고 선언하지 않는 한 명시적으로 설정이 해제됩니다 . 그리고 공백은 일종의 좋은 것입니다.주요한알 수 없는 값으로 작업할 때 다음을 수행할 수 있기 때문에 공간이 매우 좋습니다.

getopts : o -" $unknown_value"

...첫 번째 문자가 실제로 허용된 인수 문자열에 포함될 위험 없이 루프를 시작합니다. 이로 인해 getopts전체 내용이 $OPTARG한 번에 인수로 고정됩니다.

또 다른 예는 다음과 같습니다.

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

$OPTIND=1방금 사용했기 때문에 첫 번째 줄을 설정했고 getopts, 재설정할 때까지 다음 호출이 중단된 부분부터 계속되기를 기대합니다. "${arg2}"즉, 원합니다. 하지만 기부할 마음도 없고 지금은 다른 일을 하고 있어서 $OPTIND어느 시점에 가도 좋은지 재설정하여 알려드립니다 .

여기서 나는 zsh선행 공백에 대해 의문을 제기하지 않고 사용했으므로 첫 번째 문자는 8진수 40(공백 문자)입니다. getopts하지만 저는 보통 그런 식으로 사용하지 않습니다 . 저는 주로 다음과 같은 용도로 사용합니다.피하다각 바이트에 대해 a를 수행 하고 대신 위에서 패션 write()처럼 했던 것처럼 변수로 제공되는 출력을 다른 쉘 변수에 할당합니다 . set그런 다음 준비가 되면 전체 문자열을 가져오고 일반적으로 첫 번째 바이트를 제거합니다.

답변4

sed내 마음 속에 가장 먼저 떠오르는 것입니다.

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

5개의 캐릭터를 매칭하고, 6번째 캐릭터를 캡처하고, 캡처한 캐릭터로 모두 교체하세요.

그러나 문자열 길이가 정확히 6의 배수가 아닌 경우 문제가 발생합니다.

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

sed하지만 다음 과 같이 약간 변경하여 이 문제를 해결할 수 있습니다 .

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

정규식의 욕심 ​​많은 특성으로 인해 가변 길이 일치는 최대한 일치하며 캡처할 항목이 남지 않으면 캡처하지 않고 문자만 삭제됩니다.

관련 정보