Juwan Park :: 루비에서 문자열 치환하기

루비에서 문자열 치환하기

★프로그래밍/Ruby :: 2016. 8. 17. 19:52

여기서는 루비에서 문자열 치환하는 코드를 포스팅합니다.

자, 먼저 코드를 하나 봅시다.

name = "강정호"
puts sprintf("%s는 메이저리그에서 뛰고 있다.", name)
name["강정"] = "박병"
puts sprintf("%s는 메이저리그에서 뛰고 있다.", name)
name["박병"] = "이대"
puts sprintf("%s는 메이저리그에서 뛰고 있다.", name)

이 코드를 실행시킨 결과는 다음과 같습니다.

강정호는 메이저리그에서 뛰고 있다.
박병호는 메이저리그에서 뛰고 있다.
이대호는 메이저리그에서 뛰고 있다.

다른 방법으로, gsub 함수를 사용한 방법입니다.

name = "강정호"
puts sprintf("%s는 메이저리그에서 뛰고 있다.", name)
name.gsub!("강정", "박병")
puts sprintf("%s는 메이저리그에서 뛰고 있다.", name)
name.gsub!("박병", "이대")
puts sprintf("%s는 메이저리그에서 뛰고 있다.", name)

이 코드를 실행시키면,

강정호는 메이저리그에서 뛰고 있다.
박병호는 메이저리그에서 뛰고 있다.
이대호는 메이저리그에서 뛰고 있다.

역시 같은 결과가 나옵니다.

그렇다면, 이 두 코드의 결과가 언제나 같을까요? 그렇지 않습니다. 이 두 코드에서 3번줄과 4번줄을 삭제해 봅시다.

name = "강정호"
puts sprintf("%s는 메이저리그에서 뛰고 있다.", name)
name["박병"] = "이대"
puts sprintf("%s는 메이저리그에서 뛰고 있다.", name)
name = "강정호"
puts sprintf("%s는 메이저리그에서 뛰고 있다.", name)
name.gsub!("박병", "이대")
puts sprintf("%s는 메이저리그에서 뛰고 있다.", name)

이 두 코드를 실행시키면

강정호는 메이저리그에서 뛰고 있다.
string not matched
(repl):5:in `[]='
(repl):5:in `<main>'
강정호는 메이저리그에서 뛰고 있다.
강정호는 메이저리그에서 뛰고 있다.

첫 번째 코드는 오류를 내 버리지만 두 번째 코드는 정상적으로 실행됩니다.

왜냐하면, 첫 번째 방법은 문자열을 배열처럼 취급하는 방법이라 문자열이 없으면 오류를 내지만, 두 번째 방법은 문자열을 검색해서 없으면 그냥 지나치기 때문에 정상적으로 실행되는 것입니다.

또 다른 점을 보겠습니다.

names = ["LA 다저스와 LA 다저스", "LA 다저스와 LA 다저스"]
puts names
puts
names[0]["다저스"] = "에인절스"
names[1].gsub!("다저스", "에인절스")
puts names

이 코드를 실행시키면

LA 다저스와 LA 다저스
LA 다저스와 LA 다저스

LA 에인절스와 LA 다저스
LA 에인절스와 LA 에인절스

이렇습니다. 첫 번째 방법대로 하면 맨 처음 하나만 치환되지만, 두 번째 방법대로 gsub를 사용하면 조건에 맞으면 전부 치환된다는 차이점이 있습니다.

그럼 gsub 쓰고 첫 번째만 치환하려면 어떻게 하면 되냐고 의문이 드실 분이 계실텐데, 그럴 땐 gsub 대신 g를 빼고 그냥 sub라고 써 주시면 됩니다.

names = ["LA 다저스와 LA 다저스", "LA 다저스와 LA 다저스"]
puts names
puts
names[0].gsub!("다저스", "에인절스")
names[1].sub!("다저스", "에인절스")
puts names

이 코드를 실행시키면

LA 다저스와 LA 다저스
LA 다저스와 LA 다저스

LA 에인절스와 LA 에인절스
LA 에인절스와 LA 다저스

이렇게 sub 앞에 g가 붙고 안 붙고의 차이가 나타납니다. 즉, gsub는 전부 다, sub는 처음 맞는 거 하나만입니다.

여기서 잠깐, gsub 끄트머리에 왜 느낌표(!)를 붙이는가 하는 의문이 들 수 있겠습니다.

그러면 다음 코드를 봅시다.

names = ["강정호"]
names[1] = names[0].gsub("강정", "박병")
names[2] = names[0].gsub!("강정", "이대")
puts names

2번 줄과 3번 줄을 보면 하나는 gsub 뒤에 느낌표가 없고 다른 하나는 gsub 뒤에 느낌표가 있습니다.
이 코드를 실행시키면

이대호
박병호
이대호

이렇게 나옵니다. 왜 그럴까요?

그것은 gsub 뒤에 느낌표가 붙지 않으면 그 결과를 원래 변수에 대입하지 않기 때문입니다.
그러니까 2번 줄처럼 하면 names[0]의 '강정'을 '박병'으로 바꾼 '박병호'라는 문자열을 names[1]에 대입하면서 names[0]의 값은 그대로 '강정호'로 유지됩니다.
반면, 3번 줄처럼 뒤에 느낌표를 붙이면 우선 names[0]에서 '강정'을 '이대'로 바꾼 '이대호'라는 문자열을 즉시 대입하고 곧바로 names[2] = names[0] 처리가 됩니다. 그렇기 때문에 names[2]에 대입되기 전에 names[0]의 값이 먼저 바뀌어 이런 결과가 나오는 것입니다. 이것은 비단 gsub 뿐만 아니라 뒤에 느낌표가 붙고 안 붙고의 차이가 있는 모든 함수가 다 그렇습니다.

그럼 저기서 코드를 한 줄 더 써 넣어서

names = ["강정호"]
names[1] = names[0].gsub("강정", "박병")
names[2] = names[0].gsub!("강정", "이대")
names[0].gsub!("이대호", "김현수")
puts names

이렇게 하고 실행하면 과연 어떻게 될까요?

김현수
박병호
김현수

이렇게 나옵니다. 분명히 첫 번째 이대호만 김현수로 바뀌어야 할 것 같은데 이상하게 둘 다 김현수로 바뀌었습니다. 왜 그런 것일까요?

이는 루비의 변수 참조 관계 때문에 그렇습니다. 루비에서는 a=b 하면 단순히 b의 값을 a에 대입하는 것이 아니라, b의 값이 a에 대입되면서 동시에 두 변수 사이의 참조 관계가 발생하기 때문에 그렇습니다. 이는 위 코드의 3번째 줄과 같은 경우에도 gsub!의 결과물을 마치 변수처럼 취급하여 마찬가지로 참조 관계가 생기게 됩니다.

그렇다면, '한번 해병은 영원한 해병'이라는 말처럼 한번 참조 변수는 영원한 참조 변수일까요? 절대로 그렇지 않습니다. 바로 앞의 코드에서 또 한 줄을 추가해 봅시다.

names = ["강정호"]
names[1] = names[0].gsub("강정", "박병")
names[2] = names[0].gsub!("강정", "이대")
names[0].gsub!("이대호", "김현수")
names[0] = "류현진"
puts names

이렇게 하고 실행하면 과연 어떻게 될까요?

류현진
박병호
김현수

이렇습니다.

참조 관계에 있는 변수라도 그 중 한 변수에 새로운 값을 대입하거나 참조 관계가 없는 다른 변수를 참조하게 되면 기존의 참조 관계는 소멸하게 됩니다. 여기서는 names[0]과 names[2]가 '김현수'라는 문자열을 가지고 서로를 참조하고 있었지만 names[0]에 '류현진'을 대입함으로써 names[2]와의 참조 관계가 소멸하였고 따라서 names[2]의 값은 '김현수'로 유지되고 있는 것입니다.

다만,

names = ["강정호"]
names[1] = names[0].gsub("강정", "박병")
names[2] = names[0].gsub!("강정", "이대")
names[0].gsub!("이대호", "김현수")
names[0]["김현수"] = "류현진"
puts names

이런 경우는 실행 결과가 다음과 같습니다.

류현진
박병호
류현진

부분적 대입에 대해서는 기존의 참조 관계가 유지되므로 이런 결과가 나옵니다.


sub나 gsub는 다음과 같은 방법으로도 쓸 수 있습니다.

abc = ["Hello, world!", "강정호는 피츠버그 파이리츠에서 뛰고 있다."]
puts abc
puts
abc[0].gsub!(/[aeiou]/, "*")
abc[1].gsub!(/강정호|피츠버그 파이리츠/, "강정호" => "박병호", "피츠버그 파이리츠" => "미네소타 트윈스")
puts abc
Hello, world!
강정호는 피츠버그 파이리츠에서 뛰고 있다.

H*ll*, w*rld!
박병호는 미네소타 트윈스에서 뛰고 있다.


이상입니다.

많은 도움 되셨나요?

유용한 정보로 활용하시기를 바랍니다.

댓글을 달아 주세요.

Today 27    Yday 25    Tot 73,535
Juwan Park
Juwan Park's blog is powered by Daum and TISTORY.
Contemporary Blue for TISTORY.
Designed by Juwan Park. Creative Commons License
▲ TOP