ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Programming Ruby (32) 문자 인코딩
    Ruby 2016. 11. 23. 22:33
    출판사, 원작자의 저작권의 문제 소지가 발생하는 경우 본 게시물은 삭제될 수 있습니다.






    문자 인코딩 


    루비 1.9 이전에는 루비 기본 인코딩이 아스키 문자 인코딩이었다. 루비 1.9 부터 인코딩 시스템을 제공하는데 그 핵심에는 Encoding 클래스가 있다. Encoding 클래스의 객체들은 각각 서로 다른 인코딩 방법을 나타낸다. Encoding.list 메서드는 내장된 인코딩 목록을 반환하며, Encoding.aliases 메서드는 키가 별칭, 값이 대응하는 베이스 인코딩으로이루어진 해시를 반환한다. 


    encoding/list_encodings.rb

    encodings = Encoidng

                            .list

                            .each.with_object({ }) do | enc, full_list |

                                full_list[enc.name] = [enc.name]

                            end


    Encoding.aliases.each do | alias_name, base_name |

        fail "#{base_name} #{alias_name}" unless encodings[base_name]

        encodings[base_name] << alias_name

    end


    puts(encodings

                .values

                .sort_by { | base_name, *| base_name.downcase }

                .map do | base_name *rest |

                    if rest.empty?

                        base_name

                    else 

                        "#{base_name} (#{rest.join(', ')})"

                    end

                end)


    루비는 인코딩을 동적으로 읽어들인다. 이는 문자열 정규표현, 심벌, I/O 스트림 프로그램 소스 파일은 모두 이 중 특정 인코딩 객체와 연관되어 있다. 루비 프로그램에 자주 사용되는 인코딩으로는 아스키(7비트 문자), ASCII-8비트, UTF-8, Shift JIS가 있다.




    소스 파일


    인코딩은 소스파일의 속성이며, 파일이 사용되는 환경과는 상관이 없다. 루비에서는 새로운 매직 코멘트(magic comment)를 사용해서 파일마다 인코딩을 설정할 수 있다. 파일 맨 앞의 주석 부분에서 coding: 문자열을 찾는다. 이 문자열을 찾으면 공백을 건너뛰고 인코딩 이름을 검색한다. 소스 파일이 UTF-8 인코딩이면 다음과 같이 주석을 기록한다.


    # coding: utf-8


    쉬빙행이 있는 경우엔 인코딩 주석은 두 번째 줄에 작성한다.

    #!/usr/local/rubybook/bin/ruby

    # encoding: utf-8




    루비 1.9 대 루비 2.0


    루비 1.9에서 기본 소스 파일 인코딩은 US-ASCII 였다. 따라서 소스 파일에 127비트를 넘는 문자가 하나라도 있으면 루비에게 파일의 인코딩을 알려줄 필요가 있었고, 명시 하지 않은 경우 예외를 발생시킨다. 그러나 루비 2.0 에서는 기본 소스 파일 인코딩을 UTF-8로 간주하며 앞선 예제는 문제없이 실행된다.




    인코딩을 가지는 소스 요소 


    문자열 리터럴은 항상 그 문자열의 내용에 관계없이 그 문자열을 포함하는 소스파일의 인코딩을 사용해 부호화(encoding) 된다. 7비트 문자만으로 구성된 심벌과 정규 표현식 리터럴은 US-ASCII로 부호화된다. 그 외에는 심벌이느 정규 표현식이 포함된 파일의 인코딩을 사용한다.


    \u  이스케이프를 사용해서 문자열이나 정규 표현식 안에서 특정한 유니코드 문자열을 만들 수 있다. 이 이스케이프는 두 가지 형식을 가진다. \uxxxx를 사용하면 네 개의 16진수를 통해 부호화하며 \u{x..x..x..}를 사용하면 각 문자가 가변 길이의 16진수로 구성된 과변 길이의 문자를 지정할 수 있다.


    # encoding: utf-8

    "Greek pi: \u03c0"

    "Greek pi: \u{3c0}

    "Greek pi: \u{70 69 3a 20 3c0}


    \u 시퀀스를 포함하는 리터럴은 소스 파일의 인코딩과 무관하게 항상 UTF-8로 부호화된다. String#bytes 메서드는 문자열 객체 내의 바이트 구조를 파악하는데 편리하다. 다음 코드는 16비트 코드 포인트르 2바이트 UTF-8 인코딩으로 변환한다.


    #encoding: utf-8

    "pi: \u03c0".bytes # => [112, 105, 58, 32, 207, 128]




    8비트 클린 인코딩


    루비는 ASCII-8비트라는 가상 인코딩을 지원한다. 이 인코딩 이름에는 ASCII가 들어있지만, 바이너리 데이터를 포함하는 데이터 스트림에서 사용하기 위한 인코딩이다. 따라서 이 인코딩의 별칭은 BINARY이다. 소스 파일 인코딩으로 사용하면 비트가 128보다 작은 문자는 아스키 문자로 부호화하며 그 이외의 문자는 변수 이름에서 유효한 구성 요소로 간주한다. 이는 자신도 모르는 인코딩을 사용할 수 있게 해주는 특별한 기법이다. 상위 비트가 설정된 문자는 출력 가능한 문자로 간주한다.


    # encoding: ascii-8bit

    π= 3.14159

    puts "π= #{π}"

    puts "Size of 'π' = #{'π'.size}"


    실행결과

    π = 3.14159

    Size of 'π' = 2


    출력된 마지막 줄은 소스 파일 인코딩으로 ASCII-8비트를 사용하는게 위험한 이유를 보여준다. UTF-8 인코딩에서 사용하는 문자라는 것을 모르기 때문에 두 글자로 취급되기 때문이다.




    파일 단위 소스 인코딩


    규모가 큰 애플리케이션은 다수 소스 파일들로 구성된다. 이럴 때는 각 파일들에서 사용된 모든 인코딩을 제어할 수 없게 된다. 루비에서는 프로젝트를 구성하고 있는 파일들이 서로 다른 인코딩을 가질 수 있도록 허용한다. 각 파일은 독립적인 인코딩을 가지며 문자열 리터럴은 다른 파일들에서 사용되더라도 각각의 고유의 인코딩을 가지고 있다. encoding 주석은 단지 파일 내의 문자를 해석하는 방법과 아스키 이외의 문자를 포함하는 문자열 리터럴이나 정규 표현식에서 사용하는 인코딩을 루비에게 알려줄 뿐이다. 루비는 문자를 읽어 들일때 소스 파일의 실제 바이트를 절대로 변경하지 않는다.




    트랜스코딩 


    문자열, 심벌, 정규 표현식에는 인코딩 정보가 함께 저장되어 있다. 문자열의 인코딩은 String#encode 메서드를 통해 변경할 수 있다. 


    # encoding: utf-8


    ole_in_utf = "ole"

    ole_in_utf.encoding #=> #<Encoding:UTF-8>

    ole_in_utf.bytes.to_a #=> [111, 108, 195, 169]


    ole_in_8859 = ole_in_utf.encode("iso-8859-1")

    ole_in_8859.encoding #=> #<Encoding:ISO-8859-1>

    ole_in_8859.bytes.to_a #=> [111, 108, 233]


    encode 메서드를 사용할 때 변경하려는 인코딩에 문자열이 있으면 예외가 발생하므로 직접 변환할 수 없는 문자에 대해 기본 변환값을 설정해서 처리한다.


    # encoding: utf-8

    pi = "pi = π"

    puts pi.encode("iso-8859-1", :undef => :replace, :replace => "??")


    실행결과

    pi = ??


    바이너리 문자열의 인코딩 내용을 바꾸지 않고 인코딩 정보만 변환하려면 String#force_encoding 을 이용한다. 




    입출력 인코딩


    루비 I/O 객체는 데이터의 인코딩과 트랜스코딩을 모두 지원하고 있다. 즉 모든 I/O 객체는 연관된 외부 인코딩을 가진다는 의미다. 이 인코딩은 프로그램 외부에 대해 읽거나 쓰기를 할 때 사용된다. 내부 인코딩을 설정하면 입력은 읽어올 때 외부 인코딩에서 내부 인코딩으로 변환되며, 쓰기 작업을 할 때는 내부 인코딩에서 외부 인코딩으로 변환되어 출력된다. 

    OS X 시스템에서 기본 외부 인코딩은 UTF-8이므로 사용하는 모든 파일의 I/O는 UTF-8이 된다.


    f = File.open("/etc/passwd")

    puts "File encoding is #{f.external_encoding}"

    line = f.gets

    puts "Data encoding is #{line.encoding}"


    실행결과

    File encoding is UTF-8

    Data encoding is UTF-8



    I/O 객체의 외부인코딩을 강제로 지정하는 것도 가능하다 모든 문자열 다음에 콜론(:)을 붙이고 이어서 인코딩 이름을 입력하면 도니다. 단 읽어 들이는 데이터의 내용을 변환하는 방법이 아님을 주의할 필요가 있다. 단지 읽어들이는 인코딩 정보를 지정한다.



    f = File.open("/etc/passwd", "r:ascii")

    puts "File encoding is #{f.external_encoding}"
    line = f.gets
    puts "Data encoding is #{line.encoding}"

    실행결과
    File encoding is US-ASCII

    Data encoding is US-ASCII


    이하 내용들은 책을 참고.




    끄읕. 



Designed by Tistory.