Translate

2018년 7월 21일 토요일

메타프로그래밍

2011년 3월 20일 일요일

enum 형을 만들었다.
메소드 included, const_missing 을 눈여겨볼 것.

module EnumType
    @@enums = {}

    def initialize const, value
        @const = const
        @value = value
    end

    def to_i
        @value
    end

    def to_s
        "#{self.class}::#{@const}"
    end

    def self.included base
        class << base
            def const_missing const
                @@enums.include?(const) ? @@enums[const] : raise(const.to_s)
            end

            def [](value)
                @@enums[value]
            end

            def enum array
                array.each_with_index do |const, value|
                    @@enums[const] = new(const, value)
                    @@enums[value] = @@enums[const]
                end
            end
        end

        base.private_class_method :new
    end
end

class GIRepositoryError
    include EnumType

    enum [:G_IREPOSITORY_ERROR_TYPELIB_NOT_FOUND,
          :G_IREPOSITORY_ERROR_NAMESPACE_MISMATCH,
          :G_IREPOSITORY_ERROR_NAMESPACE_VERSION_CONFLICT,
          :G_IREPOSITORY_ERROR_LIBRARY_NOT_FOUND
    ]
end

p GIRepositoryError[1].object_id
p GIRepositoryError[:G_IREPOSITORY_ERROR_NAMESPACE_MISMATCH].object_id
p GIRepositoryError::G_IREPOSITORY_ERROR_TYPELIB_NOT_FOUND.class
p GIRepositoryError::G_IREPOSITORY_ERROR_TYPELIB_NOT_FOUND
p GIRepositoryError::G_IREPOSITORY_ERROR_NAMESPACE_MISMATCH.object_id
p GIRepositoryError::G_IREPOSITORY_ERROR_NAMESPACE_MISMATCH


A Sponsored Dragon-Slaying


http://mislav.uniqpath.com/poignant-guide/book/chapter-6.html#section3

Metaprogramming in Ruby


http://ruby-metaprogramming.rubylearning.com/

밥통들이 보내는 액티브X 이메일 명세서와 해법


글쓴이: hodong / 작성시간: 금, 2011-03-11 02:41

요즘 이렇게 명세서가 오더군요.
제가 제안한대로 변화하고 있습니다.

어느 날, 국민건강보험공단에서 생전 오지도 않던 이메일 명세서가 왔다. 액티브X를 설치할까 말까 고민하던 중, 이메일을 스팸으로 신고해버렸다. 기관들에게 바란다. 이메일로 명세서를 보낼 것 같으면 액티브X를 버려라.

액티브X 프로그램을 작동시키는 이메일 명세서는 문제가 많다. 특정인 상대로 주민번호, 비밀번호 같은 거 빼내기 겁나게 쉽다. 명세서.pdf 파일을 zip 으로 암호걸어서 압축하여 이메일로 보내는 것이 보안성이 더욱 우수하다.

각종 국가기관, 행정기관, 공공기관, 회사들아, 명세서를 이메일로 보낼 때, 명세서.pdf 파일을 zip 프로그램으로 암호걸어서 압축하라. 암호는 사용자가 지정해 놓은 암호 같은 걸로 하면 된다.(웬만하면 주민등록번호를 암호로 사용하지 말자.) 그리고나서 그 파일을 이메일에 첨부하여 보내라.

고객은 명세서.pdf.zip 파일을 자신의 컴퓨터에 이미 설치되어 있는 zip 압축푸는 프로그램으로 풀면 된다. 이러한 방법은 모든 플랫폼(MS 윈도우, 리눅스, Mac, 안드로이드, 아이폰 등)을 지원한다.

액티브X 이메일을 보내는 밥통들아, 얼마나 간단한가.

zip 으로 암호화하는데 사용되는 알고리즘은 AES 이다.
은행에서 사용할 정도로 강력한 알고리즘이다.

다운받은 깨진 한글 파일 이름 복구하기

인터넷에서 웹 브라우저로 파일을 받으면 서버 측의 잘못된 설정으로 인하여 한글 파일 이름이 이상하게 되는 경우가 많다.

cp949 코드가 utf-8 로 둔갑된 경우


사용자 컴퓨터 환경이 utf-8 환경이라면 그 파일 이름 자체가 utf-8 이기 때문에 iconv, convmv 로도 복구가 안 된다.
이럴 경우 아래처럼 하면 복구가 된다.
cp949(uhc)와 euc-kr 은 서로 다른 코드이다. euc-kr ⊂ cp949 이다. cp949는 euc-kr을 확장한 코드이다.

# coding: utf-8
# irb 에서 다음을 입력하면 한글이 정상적으로 표시되는 것을 알 수 있다.
"ÇÁ·Î±×·¡¹Ö¾ð¾î·Ð".unpack("U*").pack("C*").force_encoding("euc-kr").encode("utf-8") #=> "프로그래밍언어론"

"ÇÁ·Î±×·¡¹Ö¾ð¾î·Ð".unpack("U*").pack("C*").force_encoding("cp949").encode("utf-8") #=> "프로그래밍언어론"

# 아래 함수(또는 메소드)를 이용하여 프로그램을 작성하면 된다.
def repair_cp949 utf8
    utf8.unpack("U*").pack("C*").force_encoding("cp949").encode("utf-8")
end

repair_cp949 "ÇÁ·Î±×·¡¹Ö¾ð¾î·Ð" #=> "프로그래밍언어론"



cp949 코드가 URL 인코딩된 상태로 다운로드된 경우


URL 인코딩된 파일 이름으로 다운로드되는 경우도 많다.
%B9%AB%B7%E1%C0%DA%B7%E14--SRS--%B5%B6%C7%D8%BA%F1%B9%FD(%B4%DC%B6%F4%B1%B8%C1%B6)%C0%E5%B9%AE%B5%B6%C7%D8.doc 이런 파일 이름이 URL 인코딩된 파일 이름이다.

참고 링크: http://en.wikipedia.org/wiki/Percent-encoding

이러한 파일 이름을 복구해보자.

irb를 열고 다음처럼 입력해본다.

irb(main):001:0> require 'uri'
=> true
irb(main):002:0> URI.unescape("%B9%AB%B7%E1%C0%DA%B7%E14--SRS--%B5%B6%C7%D8%BA%F1%B9%FD(%B4%DC%B6%F4%B1%B8%C1%B6)%C0%E5%B9%AE%B5%B6%C7%D8").unpack("C*").pack("U*")
=> "¹«·áÀÚ·á4--SRS--µ¶Çغñ¹ý(´Ü¶ô±¸Á¶)Àå¹®µ¶ÇØ"


위에서 설명한 ÇÁ·Î±×·¡¹Ö¾ð¾î·Ð 이러한 형태의 문자열이 출력되었다. 아래처럼 하면 사람이 알아볼 수 있는 정상적인 utf-8 문자열이 출력된다.

irb(main):026:0> URI.unescape("%B9%AB%B7%E1%C0%DA%B7%E14--SRS--%B5%B6%C7%D8%BA%F1%B9%FD(%B4%DC%B6%F4%B1%B8%C1%B6)%C0%E5%B9%AE%B5%B6%C7%D8").force_encoding("cp949").encode("utf-8")
=> "무료자료4--SRS--독해비법(단락구조)장문독해"


URL 인코딩된 깨진 한글 파일 이름을 고치는 메소드를 만들어보자.

require 'uri'

def repair_url_encoded_cp949 url_encoded_string
    URI.unescape(url_encoded_string).force_encoding("cp949").encode("utf-8")
end

repair_url_encoded_cp949 "%B9%AB%B7%E1%C0%DA%B7%E14--SRS--%B5%B6%C7%D8%BA%F1%B9%FD(%B4%DC%B6%F4%B1%B8%C1%B6)%C0%E5%B9%AE%B5%B6%C7%D8"
    #=> "무료자료4--SRS--독해비법(단락구조)장문독해"


아래는 def repair_cp949(utf8) 를 활용한 디렉토리 내의 파일 이름을 고치는 소스이다.

# coding: utf-8
require 'iconv'
require 'find'

def repair_cp949 utf8
    utf8.unpack("U*").pack("C*").force_encoding("cp949").encode("utf-8")
end

Find.find(".") do |path|
    if File.file? path
        path2 = repair_cp949(path)
        `mv "#{path}" "#{path2}"` if path != path2
    end
end

파일의 각 라인을 더하는 예제 코드

2011년 1월 6일 목요일

파일 이름: data

1 2 3
4 5 6
7 8 9


루비 소스 파일 이름: test.rb

#!/usr/bin/ruby
# ruby 1.9.1 에서 테스트 했음
require 'csv'

if ARGV.length != 1
    puts "CSV file is required."
    exit
end

@sum = [0,0,0]

# col_sep 는 column separator
# 콤마로 바꾸기 위해서는 col_sep:","
csv = CSV.open(ARGV[0], "r", col_sep:" ")
csv.each do |row|
    @sum[0] += row[0].to_i
    @sum[1] += row[1].to_i
    @sum[2] += row[2].to_i
end

p @sum

결과

$ ruby test.rb data
[12, 15, 18]


csv 연산

공부할 겸 만들어봤습니다. 신버전입니다.

#!/usr/bin/ruby
# ruby 1.9.1 에서 테스트 했음

if (ARGV.length != 3) or
    ((ARGV[1] != '+') and (ARGV[1] != '-') and
    (ARGV[1] != '*') and (ARGV[1] != '/'))
    puts "Usage: $ ruby csv1 op csv2"
    puts "$ ruby csv1 + csv2"
    puts "$ ruby csv1 - csv2"
    puts "$ ruby csv1 \\* csv2"
    puts "$ ruby csv1 / csv2"
    exit
end

f1 = File.open(ARGV[0])
op = ARGV[1]
f2 = File.open(ARGV[2])

while(row1 = f1.gets and row2 = f2.gets)
    row1 = row1.split
    row2 = row2.split
    @sum = [0,0,0]
# %Q[....] 안에 있는 요거 ===> #{op} row2[0].to_i 주석문 아닙니다.
    cmd = %Q[
        @sum[0] = row1[0].to_f #{op} row2[0].to_i
        @sum[1] = row1[1].to_f #{op} row2[1].to_i
        @sum[2] = row1[2].to_f #{op} row2[2].to_i
    ]
    eval(cmd)
    puts "#{@sum[0]} #{@sum[1]} #{@sum[2]}"
end


결과

$ cat data1 data2
1 2 3
4 5 6
7 8 9
10 11 12
13 14 15
16 17 18
$ ruby test.rb data1 + data2
11.0 13.0 15.0
17.0 19.0 21.0
23.0 25.0 27.0
$ ruby test.rb data1 - data2
-9.0 -9.0 -9.0
-9.0 -9.0 -9.0
-9.0 -9.0 -9.0
$ ruby test.rb data1 \* data2
10.0 22.0 36.0
52.0 70.0 90.0
112.0 136.0 162.0
$ ruby test.rb data1 / data2
0.1 0.181818181818182 0.25
0.307692307692308 0.357142857142857 0.4
0.4375 0.470588235294118 0.5


또 다른 코드

#!/usr/bin/ruby
# ruby 1.9.1 에서 테스트 했음

# 할일 (TO DO)
# 우선 순위, 사칙 연산 파서
# for 문을 사용하여 column 을 현행 3에서 n 으로 확장할 것.
# 예외처리.

# SSV : Space Separated Value
class SSV
    def initialize(file)
        @f = File.open(file)
    end

    # op 연산자, operand 피연산자
    def compute(op, operand)
        # if 를 수식자(modifier)로 사용하면 코드가 더 간결해질 수 있지만
        # 생각하기 귀찮아서 Copy & Paste 신공으로 처리
        if operand.class == SSV
            while(row1 = @f.gets and row2 = operand.gets)
                row1 = row1.split
                row2 = row2.split
                @sum = [0,0,0]

                cmd = %Q[
                    @sum[0] = row1[0].to_f #{op} row2[0].to_i
                    @sum[1] = row1[1].to_f #{op} row2[1].to_i
                    @sum[2] = row1[2].to_f #{op} row2[2].to_i
                ]
                eval(cmd)
                puts "#{@sum[0]} #{@sum[1]} #{@sum[2]}"
            end # end of while
        else
            while(row1 = @f.gets)
                row1 = row1.split
                @sum = [0,0,0]

                cmd = %Q[
                    @sum[0] = row1[0].to_f #{op} operand.to_f
                    @sum[1] = row1[1].to_f #{op} operand.to_f
                    @sum[2] = row1[2].to_f #{op} operand.to_f
                ]
                eval(cmd)
                puts "#{@sum[0]} #{@sum[1]} #{@sum[2]}"
            end # end of while
        end # end of if
    end # end of compute

    def gets
        @f.gets
    end
end

if __FILE__ == $0
    if (ARGV.length != 3) or
        ((ARGV[1] != '+') and (ARGV[1] != '-') and
        (ARGV[1] != '*') and (ARGV[1] != '/'))
        puts "Usage: $ ruby csv1 op csv2"
        puts "$ ruby csv1 + csv2"
        puts "$ ruby csv1 - csv2"
        puts "$ ruby csv1 \\* csv2"
        puts "$ ruby csv1 / csv2"
        puts "$ ruby csv1 + 88"
        puts "$ ruby csv1 - 12.1"
        puts "$ ruby csv1 \\* 5"
        puts "$ ruby csv1 / 9.1"
        exit
    end

    ssv1 = SSV.new(ARGV[0])
    op = ARGV[1]

    # 내공이 부족한 관계로
    # 파일 이름이 숫자로 시작하면 안 됨.
    # 숫자 뒤에는 영문자 등이 오면 안 됨.
    if ARGV[2] =~ /^[0-9]/
        ssv1.compute(op, ARGV[2])
    else
        ssv2 = SSV.new(ARGV[2])
        ssv1.compute(op, ssv2)
    end
end

터미널의 개념

2011년 1월 6일 목요일

과거....
메인컴퓨터
 터미널1
 터미널2
 터미널3
 ....


이런 식으로 연결이 되어 있는데,
예를 들자면 터미널은 모니터, 키보드 이렇게 구성되어 있습니다.
이러한 터미널들이 메인컴퓨터(IBM 같은 대형 컴퓨터 같은거)에 각각 전선(꼭 전선이 아니라도 되고요) 으로 연결되어 있고요.
현대....
우리가 일반적으로 사용하는 컴퓨터 환경은

PC 컴퓨터
 마우스1
 모니터1
 키보드1


입니다.
이거를 위에 메인컴퓨터에 연결된 하드웨어 터미널처럼 에뮬레이트해주는 건데,,
터미널 창 몇개 띄우고

$ ps aux # 해보면
user   8794  0.5  0.0  20900  3980 pts/0    Ss+  23:45   0:00 bash
user   8819  2.8  0.8 323984 34088 ?        Sl   23:45   0:01 gedit
user   8820  0.4  0.0  20900  3976 pts/1    Ss+  23:45   0:00 /bin/bash
user   8848  7.3  0.0  20892  3976 pts/2    Ss   23:46   0:00 bash
user   8871  0.0  0.0  16072  1124 pts/2    R+   23:46   0:00 ps aux


이런 식으로 나오고
거기에 pts/0, pts/1, pts/2 이런 게 터미널 번호라고 보시면 됩니다.
그래서 특정의 터미널에 메시지를 보낼 때,
터미널(에뮬레이터) 창 두개 띄우고
한 쪽에서는 이렇게 하면 pts/2 터미널에 메시지가 갑니다.

$ write user pts/2

터미널 pts/2 에서 이렇게 하면
pts/1 으로 메시지가 갑니다.

$ write user pts/1

이런 식으로 메시지를 보낼 수 있어요.
user은 각각 달라도 됩니다.

DNS 설정

2011년 4월 25일 월요일

ISP에서 DHCP로 자동으로 IP와 DNS를 받아오는데 ISP에서 받아온 DNS를 사용하지 않고 다른 DNS로 사용하기 위해서는 /etc/dhcp3/dhclient.conf 을 수정하면 된다.

파일을 열어보면

#prepend domain-name-servers 127.0.0.1;

이 부분이 있을 것이다.

#을 삭제하고 127.0.0.1 대신에 자신이 원하는 주소를 넣는다. 나는 구글에서 제공하는 8.8.8.8 을 사용할 것이다.

prepend domain-name-servers 8.8.8.8;

이렇게 하면 DNS 서버로 8.8.8.8 을 사용하게 된다.

ifdown, ifup 명령을 준 후에 nslookup 으로 도메인을 확인해보면 DNS 가 8.8.8.8 변경되었음을 알 수 있다.

root@debian:/etc/dhcp# ifdown eth0
...
root@debian:/etc/dhcp# ifup eth0

root@debian:/etc/dhcp# nslookup example.com
Server:         8.8.8.8
Address:        8.8.8.8#53

Non-authoritative answer:
Name:   example.com
Address: 192.0.32.10

구문분석기 racc

racc 는 yacc 의 루비 버전이다.

calc.y

# 연산자 - *
# - 가 * 보다 우선순위가 높다
# - 는 우결합성

class CalcParser
# 연산자 우선 순위
    prechigh
        right '-' # 우결합
        left  '*' # 좌결합
    preclow

rule
    target: exp
       | {result = 0} # result 는 val[0]
    exp: exp '-' exp { result -= val[2] }
       | exp '*' exp { result *= val[2] }
       | NUMBER
end

# header, inner, footer 은 파일에 그대로 추가되는 부분이다.
---- header
require 'English'
---- inner
def parse(str) # 스캐너를 사람이 만들어야 한다.
    @q = []
    until str.empty?
        case str
        when /\A\s+/
        when /\A\d+/
            @q.push [:NUMBER, Regexp.last_match[0].to_i] # 위에 터미널인 NUMBER 가 심볼로 되어 있다.
        when /\A.|\n/o
            s = Regexp.last_match[0]
            @q.push [s, s]
        end
        str = Regexp.last_match.post_match
    end
    @q.push [false, '$end']
    do_parse
end

def next_token
    @q.shift
end

---- footer
parser = CalcParser.new
puts
puts 'type "q" to quit.'
puts
while true
    puts
    print '? '
    str = gets.chop!
    break if /q/i =~ str
    begin
        puts "= #{parser.parse(str)}" # 위에 정의한 parse 메소드가 실행된다.
    rescue ParseError
        puts $ERROR_INFO
    end
end


$ racc calc.y
$ ruby1.9.1 calc.tab.rb


-가 *보다 우선순위가 높고 -를 우결합으로 해놓았기 때문에 이렇게 계산이 된다.

1-3-2*2 = (1-(3-2)) * 2 = 0
4-1*1-2 = (4-1) * (1-2) = -3
7-5*2 = (7-5)*2 = 4
2*4-5 = 2*(4-5) = -2

응용 어플 끝글자 버그 잡는 거 진짜 개쉽습니다

그 동안 제가 끝글자 버그를 잡지 않고 방치한 이유 우선 책임, 의무가 없습니다. 제가 해당 어플 개발자도 아닐 뿐더러 오픈소스가 원래가 유지보수 의무, 보증 책임이 없습니다 . 이렇게 개떡 같은 게 오픈소스입니다. 전 과거 libhwp 하냐고...