Ruby 2.4와 함께 제공되는 새로운 기능 중에는 개선된 유니코드 지원이 있습니다. 특히 upcase
와 같은 메소드 및 downcase
예상대로 작동하고 "ä"를 "Ä"로 바꾸고 다시 되돌립니다. 이것은 저를 궁금하게 만들었습니다. André Arko의 블로그 게시물을 읽은 2013년 이후로 어떤 다른 유니코드 개선이 이루어졌습니까? Ruby의 Strings는 이제 UTF-8입니다... 맞나요?
나는 Ruby의 모든 문자열 메소드를 테스트했는데, 기술적 오류가 아니라 "최소 놀라움의 원칙" 위반 여부를 찾습니다. 구체적으로 제 가정은 다음과 같았습니다.
- 고유한 문자: "e"와 "ë"가 다른 것처럼 "e"와 "E"가 다릅니다.
- 단일 문자는 단일 문자로 간주됩니다. 유니코드로 어떻게 표현되든 상관없습니다. 이는 "e"와 "ë"가 각각 단일 문자임을 의미하지만 후자는 두 개의 코드 포인트로 표시됩니다.
- 문자는 변경할 수 없습니다. 문자열을 반대로 해도 개별 문자가 변경되어서는 안 됩니다.
- 공백은 공백으로 처리됩니다. 까다로운 유니코드 공백 문자조차도.
- 숫자는 숫자로 취급됩니다. 숫자 2는 어떻게 쓰여지든 항상 숫자 2입니다.
불행히도 대부분의 Ruby 문자열 조작 방법은 이러한 테스트에 실패합니다. 유니코드 문자열로 작업하는 경우 어떤 문자열을 사용할지 매우 주의해야 합니다.
<블록 인용>참고:출판 후 일부 독자는 내가 언급한 많은 실패가 유니코드 테스트 문자열을 정규화했다면 발생하지 않았을 것이라고 지적했습니다. 이것은 사실입니다. 그러나 문자열은 Ruby 또는 Rails(내가 테스트한 모든 앱에서)에 의해 자동으로 정규화되지 않습니다. 이 테스트는 항상 최악의 경우를 설명하기 위한 것이며 그런 점에서 여전히 유용하다고 생각합니다.
Ruby 2.4.0을 사용한 유니코드 테스트
메소드 | 테스트 | 예상 | 결과 | 평결 |
---|---|---|---|---|
#% | "%s" % "noël" | "noël" | "noël" | 알았어 |
#* | "noël" * 2 | "noëlnoël" | "noëlnoël" | 알았어 |
#<< | "noël" << "ë" | "noël" | "noël" | 알았어 |
#<=> | "ä" <=> "z" | -1 | -1 | 알았어 |
#== | "ä" == "ä" | true | true | 알았어 |
#=~ | "ä" =~ /a./ | nil | 0 | 조심하세요! |
#[] | "ä"[0] | "ä" | "a" | 조심하세요! |
#[]= | "ä"[0] = "u" | "u" | "u" | 알았어 |
#b | "ä".b.encoding.to_s | "ASCII-8BIT" | "ASCII-8BIT" | 알았어 |
#바이트 | "ä".bytes | [97, 204, 136] | [97, 204, 136] | 알았어 |
#bytesize | "ä".bytesize | 3 | 3 | 알았어 |
#byteslice | "ä".byteslice(1) | "\xCC" | "\xCC" | 알았어 |
#자본화 | "ä".capitalize | "Ä" | "Ä" | 알았어 |
#casecmp | "äa".casecmp("äz") | -1 | -1 | 알았어 |
#센터 | "ä".center(3) | " ä " | "ä " | 조심하세요! |
#문자 | "ä".chars | ["ä"] | ["a", "̈"] | 조심하세요! |
#chomp | "ä
".chomp | "ä" | "ä" | 알았어 |
#찹 | "ä".chop | "" | "a" | 조심하세요! |
#chr | "ä".chr | "ä" | "a" | 조심하세요! |
#클리어 | "ä".clear | "" | "" | 알았어 |
#코드포인트 | "ä".codepoints | [97, 776] | [97, 776] | 알았어 |
#concat | "ä".concat("x") | "äx" | "äx" | 알았어 |
#카운트 | "ä".count("a") | 0 | 1 | 조심하세요! |
#암호화 | "123".crypt("ää") == "123".crypt("aa") | false | false | 알았어 |
#삭제 | "ä".delete("a") | "ä" | "̈" | 조심하세요! |
#다운케이스 | "Ä".downcase | "ä" | "ä" | 알았어 |
#덤프 | "ä".dump | "\"a\\u0308\"" | "\"a\\u0308\"" | 알았어 |
#각_바이트 | "ä".each_byte.to_a | [97, 204, 136] | [97, 204, 136] | 알았어 |
#각_문자 | "ä".each_char.to_a | ["ä"] | ["a", "̈"] | 조심하세요! |
#각_코드포인트 | "ä".each_codepoint.to_a | [97, 776] | [97, 776] | 알았어 |
#각 줄 | "ä".each_line.to_a | ["ä"] | ["ä"] | 알았어 |
#비어 있습니까? | "ä".empty? | false | false | 알았어 |
#encode | "ä".encode("ASCII", undef: :replace) | "a?" | "a?" | 알았어 |
#인코딩 | "ä".encoding.to_s | "UTF-8" | "UTF-8" | 알았어 |
#end_with? | "ä".end_with?("ä") | true | true | 알았어 |
#eql? | "ä".eql?("a") | false | false | 알았어 |
#force_encoding | "ä".force_encoding("ASCII") | "a\xCC\x88" | "a\xCC\x88" | 알았어 |
#getbyte | "ä".getbyte(2) | 136 | 136 | 알았어 |
#gsub | "ä".gsub("a", "x") | "ä" | "ẍ" | 조심하세요! |
#해시 | "ä".hash == "a".hash | false | false | 알았어 |
#포함? | "ä".include?("a") | false | true | 조심하세요! |
#색인 | "ä".index("a") | nil | 0 | 조심하세요! |
#교체 | "ä".replace("u") | "u" | "u" | 알았어 |
#삽입 | "ä".insert(1, "u") | "äu" | "äu" | 조심하세요! |
#검사 | "ä".inspect | "\"ä\"" | "\"ä\"" | 알았어 |
#인턴 | "ä".intern | :ä | :ä | 알았어 |
#길이 | "ä".length | 1 | 2 | 조심하세요! |
#그냥 | "ä".ljust(3, "_") | "ä__" | "ä_" | 조심하세요! |
#lstrip | " ä".lstrip | "ä" | "ä" | 알았어 |
#매치 | "ä".match("a") | nil | # | 조심하세요! |
#다음 | "ä".next | "ä" | "b̈" | 조심하세요! |
# 또는 | "ä".ord | 97 | 97 | 알았어 |
#파티션 | "händ".partition("a") | ["händ"] | ["h", "a", "̈nd"] | 조심하세요! |
#앞에 추가 | "ä".prepend("ä") | "ää" | "ää" | 알았어 |
#교체 | "ä".replace("ẍ") | "ẍ" | "ẍ" | 알았어 |
#역방향 | "händ".reverse | "dnäh" | "dn̈ah" | 조심하세요! |
#r파티션 | "händ".rpartition("a") | ["händ"] | ["h", "a", "̈nd"] | 조심하세요! |
#rstrip | "line ".rstrip | "line" | "line " | 조심하세요! |
#스크럽 | "ä".scrub | "ä" | "ä" | 알았어 |
#setbyte | s = "ä"; s.setbyte(0, "x".ord); s | "ẍ" | "ẍ" | 알았어 |
#크기 | "ä".size | 1 | 2 | 조심하세요! |
#슬라이스 | "ä".slice(0) | "ä" | "a" | 조심하세요! |
#분할 | "ä".split("a") | ["ä"] | ["", "̈"] | 조심하세요! |
#스퀴즈 | "ää".squeeze("ä") | "ä" | "ää" | 조심하세요! |
#start_with? | "ä".start_with?("a") | false | true | 조심하세요! |
#스트립 | " line ".strip | "line" | " line " | 조심하세요! |
#sub | "ä".sub("a", "x") | "ä" | "ẍ" | 조심하세요! |
#성공 | "ä".succ | "b̈" | "b̈" | 알았어 |
#스왑케이스 | "ä".swapcase | "Ä" | "Ä" | 알았어 |
#to_c | "١".to_c | (1+0i) | (0+0i) | 조심하세요! |
#to_f | "١".to_f | 1.0 | 0.0 | 조심하세요! |
#to_i | "١".to_i | 1 | 0 | 조심하세요! |
#to_r | "١".to_r | (1/1) | (0/1) | 조심하세요! |
#to_sym | "ä".to_sym | :ä | :ä | 알았어 |
#tr | "ä".tr("a", "b") | "ä" | "b̈" | 조심하세요! |
#압축 풀기 | "ä".unpack("CCC") | [97, 204, 136] | [97, 204, 136] | 알았어 |
#최대 | "ä".upto("c̈").to_a | ["ä", "b̈", "c̈"] | ["ä", "b̈", "c̈"] | 알았어 |
#valid_encoding? | "ä".valid_encoding? | true | true | 알았어 |