다음 과 같은 파일이 있습니다 file.txt
.
MAL TIRRUEZF CR MAL RKZYIOL EX MAL OIY UAE RICF "MAL ACWALRM DYEUPLFWL CR ME DYEU MAIM UL IZL RKZZEKYFLF GH OHRMLZH"
문자를 다음과 같이 바꾸고 싶습니다.
M = T
A = H
L = E
C = O
R = F
E = I
X = S
(Any other letter) = _
(Anything else) = (itself)
고정 문자 재정의가 있습니다.
tr MALCREX THEOFIS < file.txt
또는:
sed 'y/MALCREX/THEOFIS/' < file.txt
하지만 제가 언급한 마지막 두 가지 규칙을 어떻게 시행할 수 있습니까?
답변1
tr
많은 실제 구현에서 문자가 첫 번째 세트에서 반복되면 마지막 인스턴스가 적용된다는 사실을 활용할 수 있다고 생각합니다 . 반복 구문과 결합하면 변환표에 나타나지 않는 문자를 명시적으로 나열하지 않고도 이 작업을 수행할 수 있습니다.
tr의 GNU 버전과 내 Mac의 FreeBSD 기반 버전의 경우 다음과 같습니다.
tr 'A-ZMALCREX' '[_*26]THEOFIS'
회전하다
MAL TIRRUEZF CR MAL RKZYIOL EX MAL OIY UAE RICF "MAL ACWALRM DYEUPLFWL CR ME DYEU MAIM UL IZL RKZZEKYFLF GH OHRMLZH"
입력하다
THE __FF_I__ OF THE F_____E IS THE ___ _HI F_O_ "THE HO_HEFT __I__E__E OF TI __I_ TH_T _E __E F___I___E_ __ __FTE__"
물론 이는 A-Z
정확히 26개의 문자가 생성된다고 가정하고 이것이 모든 tr 구현의 모든 로케일에서 작동할지는 확실하지 않습니다. 어쨌든 tr의 GNU 버전은 원시 8비트 문자만 지원하므로 C 로케일에서 작동해야 합니다.
위 코드는 Busybox에서는 동작하지 않는데, 이는 중복된 구문을 지원하지 않기 때문인 것 같습니다. 수동으로 수행해야 합니다.
busybox tr 'A-ZMALCREX' '__________________________THEOFIS'
(즉, 밑줄 26개)
간단한 테이블 기반 구현의 경우 동일한 문자의 초기 인스턴스를 반복되는 문자로 덮어쓰는 것이 당연합니다. 구현이 다른 경우 tr
다른 답변의 솔루션을 사용해야 합니다.
답변2
다른 몇 가지 제안보다 약간 길지만 따라가기가 더 쉬울 것입니다.
첫 번째 제안: 원하지 않는 문자를 매핑한 _
다음 나머지 세트를 바꿉니다.
tr BDFGHIJKNOPQSTUVWYZ _ <file | tr MALCREX THEOFIS
THE __FF_I__ OF THE F_____E IS THE ___ _HI F_O_
두 번째 제안: 하나의 명령으로 모든 작업을 수행하세요. (GNU와 BSD는 소스 맵에서 매핑되지 않은 모든 문자를 교체하기 위해 필요에 tr
따라 대상의 마지막 문자( ) 교체를 암시적으로 반복 _
하지만 이 동작은 다음으로 인해 발생합니다.POSIX단순히명시되지 않은.)
tr MALCREXBDFGHIJKNOPQSTUVWYZ THEOFIS_ <file
THE __FF_I__ OF THE F_____E IS THE ___ _HI F_O_
답변3
나는 이 perl
대안을 제안합니다:
$ perl -pe 's/(?![MALCREX])[A-Z]/_/g;y/MALCREX/THEOFIS/' file
THE __FF_I__ OF THE F_____E IS THE ___ _HI F_O_ "THE HO_HEFT __I__E__E OF TI __I_ TH_T _E __E F___I___E_ __ __FTE__"
예측 어설션을 수행하고 범위 내 모든 문자 A-Z
( 제외) 를 찾은 MALCREX
다음 명령에서 대체를 수행합니다 sed
.
~처럼스테판 차젤라스 댓글, 이 솔루션의 장점은 [A-Z]
or로 대체할 수 있어(로케일의 모든 문자를 처리하기 위해 추가할 수도 있음) 모든 유형의 문자를 처리할 수 있다는 것입니다.\w
\pL
-Mopen=locale
또 다른 접근법이 제안됨Billy 삼촌의 리뷰:
perl -pe y/MALCREXA-Z/THEOFIS_/
답변4
Raku(이전 Perl_6) 사용
raku -pe 'tr/MALCREX/THEOFIS/; s:g/ <+:Uppercase_Letter - [THEOFIS]> /_/;'
입력 예:
MAL TIRRUEZF CR MAL RKZYIOL EX MAL OIY UAE RICF "MAL ACWALRM DYEUPLFWL CR ME DYEU MAIM UL IZL RKZZEKYFLF GH OHRMLZH"
출력 예(상단에서 Raku 코드 실행):
THE TIFF_I_F OF THE F___IOE IS THE OI_ _HI FIOF "THE HO_HEFT __I__EF_E OF TI __I_ THIT _E I_E F___I__FEF _H OHFTE_H"
Raku가 이 문제를 해결하는 데 있어 한 가지 장점은 기본적으로 유니코드를 지원한다는 것입니다(그러나 이 답변에서는 언급되지 않았습니다).
위 코드의 두 번째 명령문은 s///
사용자 정의 문자 클래스를 사용한 전역 대체를 사용하며 <+:Uppercase_Letter - [THEOFIS]>
다음과 같이 더 간단하게 표현할 수 있습니다 <+:Lu - [THEOFIS]>
. @Stéphane Chazelas가 댓글에서 지적했듯이 :Lu
모든 유형의 문자를 처리할 수 있도록 or로 대체할 수 있습니다.:Letter
[\w]
제시된 간단한 사례( "
큰따옴표 및 숫자가 아닌 문자만 공백)의 경우 위 코드의 두 번째 명령문은 부사
tr///
와 함께 작성될 수 있습니다. :complement
명령문 tr:c/THEOFIS" /_/;
, 목록 에 "
큰따옴표와 공백 문자를 추가합니다.
THEOFIS
(다시 말해서,'가져가다 :complement
처음 두 문자 사이의 모든 문자 /…/
마지막 두 문자 사이에 나열된 문자로 변경합니다. /…/
, 이 경우 _
밑줄.)
raku -pe 'tr/MALCREX/THEOFIS/; tr:c/THEOFIS" /_/;'
예제 출력:
THE TIFF_I_F OF THE F___IOE IS THE OI_ _HI FIOF "THE HO_HEFT __I__EF_E OF TI __I_ THIT _E I_E F___I__FEF _H OHFTE_H"
편집하다:
위에서 했던 것처럼 음역을 두 단계로 나누면 두 번째 단계에서 "최종" 문자를 덮어쓸 위험이 있습니다. 이를 방지하기 위해 두 단계 모두에서 소문자로 변환할 수 있습니다. 이렇게 하면 다른 사람들이 본 것과 동일한 출력이 생성됩니다(단, 아래 참조).
마지막으로 업데이트된 표/코드(아래)가 더 합리적인 출력을 제공하므로 OP가 게시한 음역 표에서 실수를 저지른 것 같습니다.
raku -pe 'tr/MALCREX/theisof/; tr:c/theisof" /_/;'
업데이트된 출력:
the __ss_o__ is the s_____e of the ___ _ho s_i_ "the hi_hest __o__e__e is to __o_ th_t _e __e s___o___e_ __ __ste__"
https://docs.raku.org/언어/regexes#Pre Defined_character_classes
https://raku.org