특정 문자를 일대일로 음역하여 일부 문자를 변경하지 않고 다른 문자를 동일한 대상 문자로 바꾸는 방법은 무엇입니까?

특정 문자를 일대일로 음역하여 일부 문자를 변경하지 않고 다른 문자를 동일한 대상 문자로 바꾸는 방법은 무엇입니까?

다음 과 같은 파일이 있습니다 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

관련 정보