2017년 10월 19일 목요일

윈도우즈7, IE 인증서 오류, 크롬의 "연결이 비공개로 설정되어 있지 않습니다." 오류, 인터넷 접속 지연

잘 연결 되던 위키백과, 다음, 카카오, 티스토리 등이 윈도우즈 7 재설치 후 갑자기 연결이 안 된다. 그리고 다른 인터넷 사이트도 연결이 느리다. 몇 초를 기다려야 연결이 된다. 잠시 그런 것이 아니라 계속 그렇다. IE와 크롬에서 아래와 같은 오류 메시지가 나온다. 아래 링크는 완벽 해법인데 이 사이트가 인증서 문제로 안 보이면 이 글을 다 읽어 보라.



1. MS Internet Explorer 인증서 오류 메시지



아니 웹 사이트 접속하는데 무슨 인증서가 필요하냐? 위험하든 안 하든 내가 알아서 한다니까? 유명한 사이트인데 왜 이런 일이 생기냐? 인증서 없으면 웹 사이트도 만들지 말란 얘기냐? 인터넷 통신은 암호 통신이 아니기 때문에 도청하면 내용이 보인다. 그래서 ID와 PW 같은 중요한 정보를 전송할 때는 암호화(자물쇠/패드락/스냅락)가 필요하다. 이 걸 풀려면 암호(비번/열쇠/키)가 있어야 한다. 이걸 확인하는 과정에 문제가 있단 얘기로 보이는데 Explorer에선 그래도 보고 싶으면 봐라 하는 기능이 있다.


2. Google Chrome 인증서 오류 메시지






그런데 인증서 정보를 보니 정상이다. 컴퓨터 날짜와 유효 기간도 문제 없다. 인증 경로를 보니 (다층 구조인 모양이네?) 문제 있는 인증서가 제대로 저장이 되어 있지 않다는 것이다. 이런 건 자동으로 하게 되어 있는데 뭔가 문제가 있는 것이다.




인증서 관리 화면에서 찾아 보니 정말 없다. 자동으로 되어야 하는데 뭔가 문제가 있는 것이다. 자동 업데이트가 안 되도록 설정하는 게 있다고 하니 확인해 보자. 레지스트리 편집기를 실행한다.




자동 업데이트 중지 설정이 되어 있지도 않다. 당연하겠지? 여기까지 왔으면 뭔가 다른 문제라는 건 알겠는데 인터넷 검색을 하거나 도움말을 찾아 보면 "이런 경우 다른 문제이니 찾아 보라"라고 나온다. ㅋㅋㅋㅋ 인증서를 수동으로 직접 관리하는 방법도 있는데 그걸로 하니 된다. (아마 마지막 해법이겠다) 문제의 인증서를 확인하고 다운 받는다. 그리고 아래 링크 방법으로 직접 등록하면 된다. 만약 아래 링크가 인증서 문제로 역시 안 보인다면? 그래서 직접 정리해 올린다.




위처럼 문제의 인증서는 쉽게 파일로 다운 받을 수 있다. 이제 이걸 수동 등록하는 방법을 알아야 하는데 윈도우즈나 브라우저 도움말에서 인증서 등록으로 검색하면 방법이 나온다. 설명을 읽어 보면 할까 말까 고민이 될 것이다. 그림이 없이 글로만 되어 있으니까 뭐가 뭔지 모른다. 명령어로 실행하는 창에서 Windows 7 경우 "mmc"를 직접 타이핑하여 입력하면 등록할 수 있는 화면이 나온다. 그 화면 조작 방법만 알면 된다.




참으로 복잡한 과정을 거치는데 정말 피곤하다. 그냥 따라하면 된다. 인증서는 내 컴퓨터에 설치하는 것이다. 그리고 내 컴퓨터 사용하면 누가 사용하든 무조건 되게 해야 한다. 그리고 인증서를 우측 클릭하면 드디어 인증서를 가져올 수 있게 된다. 그 과정은 아래와 같이 단순하다. 다운 받았던 인증서 파일 찾고 그걸 어디에 등록할지 결정하면 끝이다.








3. Opera 인증서 오류 메시지



4. FireFox에선 문제 없이 되더라! ㅋㅋㅋ 왜지?


옛날에 할 수 없이 인터넷 서비스 업체의 원격 지원을 받아 보니 (원격 지원일지 개인 정보 유출일지 잘 모르겠지만) IE에 설치되어 있는 쇼핑몰 프로그램이 충돌을 일으킨 거로 보인다. 이 쇼핑몰 프로그램이 중간에 장난질을 친 것 같다. 이것들을 모두 제거하고 기타 뭔가 돕겠다는 정체 모를 도움이(Helper)도 일단 제거한다. A와 B 사이에 껴서 인터넷 감시를 하거나 바이러스 확인을 하는 것들인데 정체를 모르면 일단 제거한다. (순수한 컴퓨터가 좋다!) 그리고 또 뭔가를 어떻게 했다. 그 뭔가가 기억 안 났는데 인터넷에서 찾았다.


그랬더니 정상 동작이 되더라. 위의 물건들이 원인인가? 닷넷 라이브러리인데? 이것들 사용 안 하면 모든 것이 정상적으로 동작한다. 아이고 정말 힘들다. 그러나 다음에 윈도우즈 재설치 하고 뭔가 다른 프로그램을 설치하면 또 다른 사이트가 안 된다. 그러니까 원인이 뭔지 모르겠다. 안 되는 사이트도 때에 따라 다르다. 브라우저에 따라 또 다르다. 뭐지?


내가 볼 때는 브라우저 업체, 인증서 업체, 웹 사이트 업체, 정부 기관 사이의 싸움 같아.

  1. 브라우저 업체 : 타사 브라우저 죽이기? 웹 사이트 업체 뜯어 먹기?
  2. 인증서 업체 : 앉아서 도장 찍어주는 회사!? 웹 사이트 업체 뜯어 먹기?
  3. 웹 사이트 업체 : 인증 받아야 하는 입장!? 당하는 자? 고객 털어 먹기?
  4. 정부 기관 : 특정 국가 정부는 이런 저런 이유로 인터넷 검색을 차단한다

저들끼리 뭔가 마음에 안 들면 왕따 시키기 하는 것 같아. 왜냐?
왜 특정 업체 관련 사이트들만 동시에 차단 당하는가 말이지.
그거야 같은 인증 기관을 이용하니까. 
그럼 인증 기관이 문제인 거야? 아마도...

2017년 9월 10일 일요일

프리킹, 피싱, 스미싱, 보이스 피싱- 모르는 사람들이 항의 전화? 모르는 내용의 비용 청구?

★ phreaking[프리킹] = 《구어》 전화 회선망의 부정사용; ⦗일반적⦘ 네트워크 등에의 침입.


옛날에 전화망을 이용한 해킹이 많이 있었다고 함. 영화 터미네이터에도 등장함. 전화번호를 속이는 것은 쉽게 하는 것 같음. 마찬가지로 후대에 나온 인터넷 IP 주소 속이는 것도 쉽다. 즉 다른 사람이 범죄를 저지른 것처럼 만들 수 있다는 것. 진짜 위치는 교환기/라우터 등을 통해서 경로 정보를 입수해야 알 수 있음. 전화 번호나 IP 주소만 보고는 모름.



★ phishing[피싱] = private data + fishing = 개인 정보 낚시 = 전자우편/메신저로 낚기


PC를 사용한 전자우편이나 메신저로 바이러스로 가는 링크를 걸어 바이러스 프로그램을 다운로드하게 하거나 직접 계좌번호, 신용카드 번호, 비밀번호를 보내도록 하여 개인 정보를 빼감. 보통 사람은 당하지 않는데 정보가 둔한 노인, 주부, 모지리, 띨띨이, 보수꼴통은 낚일 수 있다. 은행, 우체국, 경찰, 검찰 등이라고 하면 믿지 말 것.

중요한 정보는 우편물로 수신자가 직접 받도록 전달하거나 전화 통화를 하거나 직접 찾아오지 전자우편이나 문자 메시지를 보내지 않음. 경찰이라고 하더라도 가짜 경찰복인지 잘 확인해야 함. 즉 경찰서에 다시 전화해서 진짜인지 물어 볼 것. 영장 없이는 집에 들어오거나 체포할 수 없으니 진짜 경찰서에 물어 볼 것.



★ Smishing[스미싱] = SMS + phishing = 휴대폰 문자 메시지로 개인 정보 낚기


※ SMS = Short Message Service : 짧은 문자 서비스

휴대폰의 SMS 문자 메시지를 통해서도 링크를 걸어 바이러스를 다운로드 받게 만들어서 개인 정보를 빼감. 헌데 주로 전화번호를 알아가서 아무짝에 쓸모없는 광고 문자를 불특정 다수에게 무작위로 보내거나 지인들에게도 같은 바이러스를 뿌림. 암호는 암호화 되어 있어 알아 내기 힘들다.

정보가 둔한 노인, 주부, 모지리, 띨띨이, 보수꼴통들이 스미싱인지 모르고 피해자에게 항의 전화를 하니 모르는 사람의 전화가 오면 받지 말고 무시할 것. 통화해 보면 알겠지만 말이 잘 통하는 상대들이 아님. 약 빨았거나 술 취했거나 그런 상태임.

※ 보통 도박, 매춘, 포르노 사이트 광고이기 때문에 문자 광고 내용을 알려 달라고 해도 창피해서 잘 안 알려주니 증거 자료 얻기도 힘들다. 그러니 상대하지 말라.

광고주가 누구인지도 소개하지 않는 광고를 광고라고 할 수 있을까? 당연히 문자 보낸 사람을 광고주라고 생각하겠지. 결국 쓰레기 정보를 보내서 피해자 골탕 먹이자는 것임. 경찰 말에 의하면 보통 이벤트성 광고는 2~3일이면 끝난다고 함. 그 이상이면 고의적인 악성 문자 테러인데 돈도 안 되는 범죄를 하는 범죄자는 없음. 고로 이건 불순한 정치적 의도를 가진 보수 꼴통 단체 소행임.

안드로이드 폰의 경우 스미싱 차단 프로그램(알약 등)을 정식 루트(구글 플레이)를 통해 설치할 것. 비정식 루트로 프로그램을 다운로드 받으면 100% 바이러스에 걸림. 정식 루트를 통한 프로그램도 진짜를 사칭한 바이러스일 가능성 있음. (믿을 놈 없다는 것) 통신사에서 제공하는 스미싱 차단은 소액 결제 차단임.

역시 아이폰이 더 안전한가?

여하튼 결론은 문제가 계속되면 전화 번호 바꾸는 것 외엔 답이 없다.
(경찰과 통신사는 이런 일로 범인을 잡는데 협조 하지 않는다.)

인터넷, 전화망으로 결제하는 짓은 절대 하지 마라.
(이렇게 설계를 하고 쓰라고 하면 누가 쓰겠는가?)



★ voice phishing = 직접 통화로 바보들 속여 스스로 비밀번호 불게 만들기


대담하게 직접 전화를 걸어서 경찰, 검찰, 은행 등을 사칭하면서 계좌 번호와 비밀 번호를 물어 보는 것인데 보통 사람들은 잘 안 걸리고 정보가 둔한 노인, 주부, 모지리, 띨띨이, 보수꼴통들이 주로 낚임. 항상 인류 중에 10%는 좀 모자라기 때문에 사기 당하는 사람이 주로 또 사기 당함. 지체 장애보다 더 무서운 것이 지적 장애임. 아무리 예뻐도 머리가 나쁘면 결혼하지 말 것.

경찰, 검찰이라고 하더라도 “좆 까!”라고 할 수 있는 성숙한 민주 시민이 되자.



이 문제 해결엔 경찰, 통신사, 독재 잔당을 족쳐야 한다.






한국엔 박정희교라는 사이비 종교에 빠진 사람들이 무시 못 할 정도(약 40%)로 많다. 주로 정보가 둔한 노인, 주부, 모지리, 띨띨이, 보수꼴통들이다. 구세주 박정희가 탄생해서 경제 성장 기적을 일으켰다는 신화를 중심으로 하고 있음. 이 사이비 종교에 빠진 사람들은 우리가 먹는 식량과 쓰는 공산품까지 박정희가 마술로 만들었다고 주장함. 그리고 세상엔 박정희 무리와 적박정희 무리(빨갱이)만 있다고 생각함. 허나 박정희 얘기만 안 나오면 발광하지 않으니 안심할 것. 박정희 얘기를 하더라도 돌려 말 하면 무슨 소리인지 몰라 얌전하니 안심할 것. 그러니까 박정희 이름만 빼면 박정희 욕을 해도 모름.

2017년 9월 7일 목요일

안드로이드 폰 바이러스 - 인터넷 접속 불량, 계속 크롬을 업그레이드 하라?

웹사이트에 접속하다 보면 오타가 발생할 수 있다. 구글과 비슷한 이름의 엉뚱한 사이트로 연결되는데 거기서 뭐라 유혹하면서 버튼을 누르라고 해서 아마도 눌렀을 것이다. 첫 번째 바이러스가 침투했다.

정확하게 www.google.com 또는 www.google.co.kr 라고 입력해야 한다. 비슷한 이름의 엉뚱한 사이트로 가는 것을 막으려면...

이 바이러스는 무선 LAN을 통해 여러분 공유기(게이트웨이)에 접속을 한다. 이 때 만약 여러분 게이트웨이에 암호가 걸려 있지 않다면 얘가 마음대로 게이트웨이 설정을 바꿀 수 있다. DNS 서버의 주소(전화로 말하면 114)를 바꾸어 엉뚱한 곳으로 인도한다.

브라우저에서 게이트웨이(공유기) 주소를 입력하면 당신도 게이트웨이를 설정할 수 있다. 암호가 걸려 있지 않다면 외부인 아무나 그 설정을 건드릴 수 있다. 무선이라면 더욱 쉽다. 그러니 공유기 관리자를 등록하고 암호를 걸어라. 문지기가 암호가 없다는 것은 빤스도 안 입고 싸우겠다는 것이다.

와이파이와 데이터 네트워크가 멀쩡함에도 인터넷을 사용하려고 하면 제대로 웹 사이트에 연결되지 않거나, 엉뚱한 곳으로 가거나, 끝도 없이 크롬을 업그레이드 하라고 하면 이 바이러스에 걸린 것이다.

삼성 A/S 센터에 가서 문의를 했는데 거기선 이상이 없었다. 그런데 집에 와 보니 같은 문제가 생겼다. 장소에 따라 다르다? 뭐가 차이가 있는 것일까? 거기와 여기의 공유기(게이트웨이)가 다른 것이다. 공유기의 DNS 주소(전화 번호부 114)가 달라 엉뚱한 곳으로 안내하는 것이다.

※ 인터넷 DNS = 전화망 114 = 이름(문자주소)을 물어 보면 번호(숫자주소)를 알려 준다.
※ 공유기 = 허브 + 라우터 + 파이어월 + 게이트웨이



해결 방법은?


  1. 명령 프롬프트에서 ipconfig로 게이트웨이 주소를 확인한다.
  2. 웹브라우저 주소 창에 게이트웨이 주소를 넣어 접속한다.
  3. 게이트웨이를 초기 설정으로 돌려놓고 ID/PW를 걸어둔다.
  4. 안드로이드 폰의 백신으로 바이러스를 제거한다. (크롬으로 위장)
  5. 크롬이 2개 있을 것인데 그 중 하나가 바이러스(트로이목마)이다.





아주 모르는 자(순진무식)와 뭔가 아는 자의 차이는 하늘과 땅 차이다.
오답을 아는 자(보수꼴통)와 정답을 아는 자(진보분통)의 차이는 종이 한 장 차이다.
선(금)과 악(똥)을 반대로 알고 있었다면 손바닥 뒤집듯이 생각만 바꾸면 된다.
그런데 전혀 모르는 자는 그 둘을 구분할 수도 없다.

2017년 4월 28일 금요일

아주 간단한 어셈블리어, 인터프리터, 컴파일러 제작하기?

힘들게 공룡과 기사가 그려진 컴파일러란 책을 보고 깨달은 것이다. 책은 정말 두꺼운데 내용을 요약하면 10% 정도로 줄겠더라. 정말 읽기 피곤할 정도로 난잡하게 쓴 책이다.



어셈블리어란?


기계어란 것은 컴퓨터가 이해하는 명령어로 숫자로 되어 있다. 이 숫자를 사람이 읽기 피곤하기 때문에 단어로 바꾼 것이 어셈블리어이다. 예를 들면 다음과 같은 꼴이다. 어떤 CPU의 명령어라고 하자.

9797 1234 5678 = Add $1234, $5678 = Acc ← $1234 + $5678

9797(꾸질꾸질) = Add = 더하라, $는 수가 아니라 주소를 의미. 1234, 5678은 메모리 주소이다. 내용은 1234번지와 5678번지 내용을 더해서 Acc 레지스터에 넣어라 하는 더하기 명령이다.

반복문과 조건문은 어셈블리어에서 보면 본질적으로 같다. 조건 판단(크다/작다/같다/다르다)을 하는 부분과 거기에 따라 실행할 명령어가 담긴 메모리 주소로 이동(점프)하는 부분으로 되어 있다. 그래서 어셈블리어에서도 조건문과 반복문은 고급언어처럼 보이게 할 수 있다. 반복문과 조건문 번역은 그렇게 어렵지 않다.

고급 언어와 어셈블리어 언어의 차이는 아마도 수식을 번역할 수 있느냐 없느냐 차이 정도? 정말 단순한 어셈블리어는 단어 vs 숫자 번역기 같은데 복잡한 어셈블리어는 수식도 번역해 준다. (인문계 친구에게 수학도 언어라고 얘기했더니 노발대발 하던데... 컴퓨터 분야에선 수학도 언어의 일종이라 언어 처리 기술을 사용한다. 유명한 과학자가 한 말이 있는데 수학은 우주를 표현하는 언어라고 하더라.)



인터프리터란?


고급언어에서 문제는 반복문, 조건문이 아니라 사람이 쓰는 수식을 기계가 이해하기 쉬운 순서로 바꾸는 것이다. 예를 들면 아래와 같다.

1234 + 5678 ⇒ + 1234 5678  ⇒ Add 1234, 5678 ⇒ Add(1234, 5678)

인간의 표현에선 연산자가 중간에 온다. 이걸 제일 앞에 나오게 하는 접두사, 제일 뒤에 나오게 하는 접미사 표현으로 바꾼다. 접두사 표현은 어셈블리어와 비슷한 영어식 어순이고, 함수 표현과도 같다. 접미사 표현은 한국어 어순과 같은 것이다.

1234 + 5678 ⇒ 1234 5678 +  ⇒ 1234와 5678을 더해라

이렇게 수식을 번역하고 바로 실행하는 것을 인터프리터라 한다. 고로 매번 실행할 때 번역 시간이 소모된다. 이 소모 시간을 피하려면 번역한 내용을 지우지 말고 재활용해야 하는데 메모리가 그만큼 소모된다.

반복문과 조건문 번역은 수식 번역만큼 오랜 시간이 걸리지 않는다. 고로 수식 번역 결과 정도만 따로 보관해도 처리 속도는 향상될 것이다. 아니면 반복문과 조건문까지 포함해서 번역한 것을 일정 단위로 저장했다가 재활용하는 것이다.

인터프리터의 장점은 메모리 절약 정도? 쉽게 코드를 수정할 수 있다는 것? 프로그램을 조립해서 사용하기 쉽다는 정도? 느리다는 것을 빼면 다 장점이다.

※ Java(자바)는 가상 머신 위에서 동작하는 인터프리터 언어이다. 그러니까 가상 머신의 코드를 진짜 머신의 코드로 바꾸는 과정이 필요하다. 그래서 실행 속도가 느리다. 그 경쟁자 C#은 가상 머신 코드를 진짜 실행 코드로 컴파일 하는 것도 추가 한다.



컴파일러란?


번역한 내용을 따로 만들어 두고 그것을 실행하기 때문에 번역 시간은 1회만 소모되고 처리 속도가 빠르다. 대신 번역한 내용을 담아야 하는 메모리 소모, 코드를 일부 수정하면 전체를 다시 번역해서 다른 부분들과 조립해야 하는 번거로움이 있다. 실행 속도가 빠르다는 것을 빼면 다 단점이다. 허나 인터프리터 요소를 살짝 섞은 조립식 컴파일러가 나온다면 얘기는 달라진다. 예를 들어 모든 객체/함수를 별도로 컴파일 해 두는 것이다. 실행할 때 즉석에서 조립하여 연결하는 방법을 쓴다.

이런 경우 세상 모든 함수, 모듈, 객체에 대한 이름 구분이 필요해진다. 이놈이 만든 함수와 저놈이 만든 함수가 이름이 같으면 어느 것을 붙여야 하겠는가? 경상도 홍길동인가 전라도 홍길동인가? 보통 간단하게 같은 폴더 안에 있는 지역 함수와 공통으로 사용하는 폴더의 전역 함수만 조립하면 된다. 이렇게 할 경우 그 함수만 수정해서 컴파일 한 후 넣으면 유지보수는 간단히 끝난다.

예를 들어 다음과 같은 코드가 있다고 하자.

A = B + C
D = A * E

여기서 A, B, C, D, E 변수의 주소는 가변적일 수 있다. 프로그램을 실행시킬 때 메모리 위치를 어디로 잡느냐에 따라 실제 주소는 변할 수도 있다. 그렇다면 메모리에 읽어 올 때 변수의 주소를 결정해서 번역해 주는 과정이 필요하다. 그래서 이런 번잡한 과정을 피하고자 메모리를 블록/세그먼트/페이지/클러스터/섹터 등 (이름이 참 많다) 구간으로 나누는 방법을 쓴다.

$1 = $2 + $3
$4 = $1 + $5

이렇게 번역한 후에 블록/세그먼트/페이지/클러스터/섹터 뭐라 하든지 그것의 시작 주소만 결정해 주면 다음과 같이 자동으로 계산 처리 된다.

시작 주소 = S
$(S+1) = $(S+2) + $(S+3)
$(S+4) = $(S+1) + $(S+5)

이런 식의 실행 전에 바로 조립을 하려면 O/S의 도움 없인 불가능하다. O/S가 모든 주소를 번역하거나 시작 주소를 결정해 주어야 한다. 마찬가지로 조립할 함수들의 위치를 결정하면서 함수들의 주소도 번역해 주어야 한다. 이 과정이 거의 인터프리터가 하는 작업과 비슷한 수준이라 적절히 섞자는 것이다. 모든 주소를 번역하기는 힘들고 시작 주소만 결정해 주는 것이 좋은데 그건 O/S보다 CPU의 기능이 가능해야 한다.

※ 아마도 이와 비슷한 개념이 윈도우즈의 DLL 같은 것, 자바의 클래스 파일 같은 것이겠다. 이 뒤를 잇는 C#에서도 역시 클래스 명칭 구분을 위한 방법들이 나온다. (IT 분야엔 고수가 적고 하수가 많다. 이 명칭 구분에 대한 이해도 떨어지는 사람들이 많더라. 학원 강사라면서 말이다. 어떻게 초보자보다 못 하냐?)



단어 인식 ↔ 수식 인식 ↔ 문장 인식


예를 들어 조건문, 반복문, 선언문, 대입문은 문장이라고 한다. 그 속에 들어가는 것이 수식이다. 수식 안에 단어들이 들어 있다. 고로 단어 인식기가 가장 하위직이고 중간이 수식 인식기이며 최고 상급자가 문장 인식기가 된다.


  • 선언문이란 단어(변수/상수/함수)의 품사(문법 특징)를 정해주는 것과 같은 것이다.
  • 대입문이란 수식의 계산 결과를 단어(변수/함수)에 넣어주는 것이다.
  • 조건문이란 수식의 계산 결과를 보고 무엇을 실행할지 (어디로 갈지) 결정하는 것이다.
  • 반복문이란 수식의 계산 결과를 보고 반복할 것인지 결정하는 것이다.


수식은 우리가 아는 수학에서 수식과 같은 것이다. 그 수식 안에는 단어들이 들어 있다. 단어는 변수, 상수, 함수이다. 이것들은 공백, 기호(소수점/괄호), 연산자로 구분할 수 있다. 단어는 알파벳, 숫자, 기호, 한글, 한자 등 문자 차이를 보고 구분이 가능하다.

  • 상수 : 값이 변하지 않는다. 예) π = 3.141592, 긴 숫자보단 문자 π를 쓰면 편하지?
  • 변수 : 값이 변한다. 값이 미정이다. 열어 봐야 안다. 계산해 봐야 안다.
  • 함수 : 파라미터(매개변수)에 따라 정해진 계산 결과 값이다.

사람이 정해주는 단어에 해당하는 변수, 상수, 함수는 알파벳과 숫자의 조합이다. 함수와 변수의 차이는 함수 뒤에는 괄호가 온다는 것이다. 수학 상수 π나 e와 같은 것은 변수처럼 이름을 붙일 수 있다. 이러면 변수인지 상수인지는 단어만 보곤 알 수 없게 된다. 고로 단어장이 필요해진다. 숫자로 된 상수는 다음과 같은 패턴이다.

-123.456e-789
부호/정수.소수e부호/지수
정수/소수/지수 = 0~9의 반복

이 패턴을 보고 이 게 한 단어라는 것을 판독하는 게 단어 인식기다. 단어 인식기에는 괄호 구조 인식 기능이 없다. 수식/문장 인식기의 경우는 괄호 구조를 인식해야 한다. 즉 열었으면 닫아야 하는 구조로 되어 있다. 물론 원한다면 단어 인식기에도 괄호 구조를 넣을 수 있다. 단어의 시작(접두사)과 끝(접미사)이 어떤 궁합을 가지고 있다면 말이다. 헌데 그런 단어는 세상에 없으니 만들 필요 없다. 필요한 것이 있다면 변수, 상수, 함수를 구분해 주는 접두사 정도는 있으면 편하다.

문장 인식기는 수식이 들어갈 자리가 나오면 수식 인식기를 호출하여 수식 부분 번역을 담당하게 하거나 계산 결과를 가져오라고 한다. 수식 인식기는 단어가 나오는 부분에선 단어 인식기를 호출해서 그 단어가 어떤 품사인지 내용물은 뭔지 가져오라고 한다. 단어 인식기는 문자 하나씩 읽으면서 이질적인 문자가 나오는지 확인하여 단어를 구분해 내고, 단어장에서 검색해 본다.

단어 인식기에 많이 사용하는 방법은 상태 변화도, 상태 변화표이다. 그림으로 그리면 상태 변화도이고 이걸 표로 만들면 상태 변화표이다. 예를 들어 위의 수식 패턴에서 부호나 기호는 한 문자라서 간단한데 정수, 소수, 지수는 0~9의 반복이다. 이렇게 같은 종류의 문자가 계속 나오면 그 구간은 같은 상태로 보는 것이다.

현재 상태 + 입력 문자 → 필요 행동 + 다음 상태
if 현재 상태 and 입력 문자 then 뭔가 하고 다음 상태로 변경 end

예를 들어 정수부를 읽는 상태라면 입력 문자가 계속 0~9가 나오면 계속 정수 상태를 유지하다가 소수점이 나타나면 정수 상태는 끝나는 것이다. 나머지 소수부, 지수부도 정수부와 같기 때문에 상태만 다르지 처리 방법은 같은 식이다. (난 지금 숫자 상수를 읽고 있어, 그 중에서 정수부를 읽고 있어. 다른 기호가 나올 때까지 말이야.)

이렇게 읽은 10진수를 2진수로 바꾸려면, 일단 정수부와 소수부를 붙여 정수로 보고 2진수로 바꾼다. 여기에 부호를 적용하고, 소수점 위치만큼 10을 곱하거나 나누기 한다. 지수부도 읽은 후에 부호를 붙여 2진 정수로 바꾼 후 그 만큼 10 곱하기/나누기를 한다. 그럼 2진수 실수로 바뀌어 저장이 된다.

※ 실수의 경우 2진수는 10진수로 정확히 바뀌는데 10진수는 2진수로 정확히 바뀌지 않는다. (이상한데? 모든 수는 2로 정확히 나눌 수 있어 정확하게 바뀌는데? 메모리 한계 때문에 정밀도가 떨어진다는 의미이다.) 10진수 문자열(표현형식)과 2진수 실수(저장형식) 사이 변환 과정에서 2 곱하기 나누기를 반복하는 것보다는 한 번에 10의 거듭제곱이나 2의 거듭제곱을 곱하고 나누는 게 더 빠르다.



문법 표현


단어의 구조를 표현하는 문법이 정규 문법, 정규식이라고 한다. 수식이나 명령문을 표현하는 문법이 문맥 자유 문법이다.


  • 생성 문법 : 추상 표현(비단말) → 구체 표현 대체(단말 포함)
  • 정규 문법 = 정규 표현식(정규식) : 비말단 → 말단/{말단+비말단}/{비말단+말단}/생략
  • 문맥 자유 문법 : 비말단 → 말단&비말단 조합
  • 문맥 의존 문법 = 문맥 민감 문법 : 앞+{비말단}+뒤 → 앞+{말단&비말단 조합}+뒤
  • 재귀 열거 문법 : 재귀 열거 언어(재귀 열거 집합)를 표한다고 하는데 알 거 없다.

※ 말단/단말 : 구체적 단어/문자/기호, 비말단/비단말 : 추상적 계층(식/항/구/절)


좌측의 추상적 표현을 우측의 좀 더 구체적 표현으로 대체하면서 문장을 만들어가는 작문 문법이 생성 문법이다. 재귀 열거 문법이 뭔지 모르겠으나 형태를 단순하게 제약한 것이 문맥 의존 문법이다. 그러니까 이건 앞과 뒤를 보고 중간을 대체하여 작문을 하는 것이다. 앞 뒤 문맥을 본다는 말이다. 앞과 뒤가 안 맞으면 대체 불가능하다.

문맥 의존 문법 중에 형태를 단순하게 제약한 것이 문맥 자유 문법이다. 앞뒤 상황을 보지 않고 그냥 기계적으로 대체하면 된다. 컴퓨터 언어(수식, 명령문)는 모두 이 문법에 속한다. 이 문법으로 괄호 구조를 표현할 수 있다. 문맥 자유 문법 중에서 형태를 단순하게 제약한 것이 정규 문법이다. 비말단은 반드시 말단을 포함하며 비말단은 하나인 단순한 형태로 대체 된다. 이 문법은 괄호 구조를 표현하지 못 한다. 단어 구조 표현은 이 문법에 속한다. 이 문법을 수식처럼 표현한 것이 정규식이다.

이렇게 문법으로 표현해도 번역기를 만들 때는 직접적인 도움이 안 된다. 그 문법이란 것은 작문(생성)을 할 때 도움이 되는 문법이지 역으로 독해(해석)를 할 때는 도움이 안 된다. 독해할 때는 거꾸로 된 문법이 필요하다. 예를 들면 다음과 같은 식이다. 생성 문법을 거꾸로 읽어 적용해야 한다.

해석 문법 : 구체 표현 → 추상 표현 대체

독해를 거쳐 어떤 순서(어순)로 나열된 1차원적 배치의 문장(영어)이 순서가 없는 입체적 연결 상태(문장 성분 사이의 관계도로 나무 가지 모양)로 바뀐다. 이 연결 상태를 어떤 문법에 따라 어떤 순서로 나열된 문장(한국어)으로 바꾸는 것이 번역이다. 단순 단어 대체가 아닌 어순을 바꿀 때는 먼저 독해를 완벽하게 끝내야 한다. 일단 독해가 끝나는 시점에 이 수식/문장이 문법에 맞는지 틀린지 결판이 난다.

I am a boy → I=(a+boy) → 나=(한+소년) → 나+이다+(한+소년) → 나는 한 소년이다.

생성 문법을 어떻게 표현하든 결국 해석 문법을 보고 상태 변화도, 상태 변화표를 만들어야 구현할 수 있다. 여기서 상태는 추상적 계층이 된다. 입력은 읽은 문자/단어가 된다. 헌데 수식의 경우처럼 뒤의 연산자까지 읽어 봐야 결정이 되는 경우는 입력 값에 다음 연산자도 포함이 된다. 즉 다음 문자/단어를 읽어 봐야 어떤 결론이 나는 경우는 상태 변화도에서 다음 문자/단어도 입력으로 반영 되어야 한다. 고로 상태 변화도가 상당히 복잡해진다. 생성 문법을 보고 다음에 나올 수 있는 가능한 모든 문자/단어를 찾아야 하는 문제가 있다.

  • A + B * C → A + (B * C) : B 뒤에 곱하기가 있어 이렇게 번역 된다.
  • A * (B + C) → A * (B + C) : B 앞에 괄호가 나오기 때문에 이렇게 번역 된다.
  • A * B + C → (A * B) + C : B 뒤에 더하기가 있어 이렇게 번역 된다.




고급언어의 수식 번역과 계산


다음과 같은 접간사 형태의 수식을 접두사나 접미사 형태로 바꾸는 것을 말한다.

원문 : A = B + C * (D + E)
번역 : A ← + B * C + D E or A ← D E + C * B +
번역 : A ← 합(B, 곱(C, 합(D, E))) or A ← (((D, E)합, C)곱, B)합

영어식 어순으로 번역했을 경우 읽는 순서 그대로 계산하기 힘들다. 이건 복합문을 단문으로 나누어 계산 순서에 맞게 배치해야 계산이 가능하다. 예를 들면 아래와 같다.

순서1 : Acc ← 합(D, E)
순서2 : Acc ← 곱(C, Acc)
순서3 : Acc ← 합(B, Acc)
순서4 : A ← Acc

그런데 역순으로 읽었더니, 다시 말해서 한국식 어순으로 번역했을 경우 읽는 순서 그대로 계산하기 쉽다. 예를 들면 아래와 같다.

0단계 : (((D, E)합, C)곱, B)합 = D, E, 합, C, 곱, B, 합
1단계 : Acc ← D, E, 합
2단계 : Acc ← Acc, C, 곱
3단계 : Acc ← Acc, B, 합
4단계 : A ← Acc

그러니까 어떻게 하든 원문을 이 순서로 번역만 하면 되는 것이다. 방법은 2가지가 있는데 책에서 설명하는 아주 복잡한 방법으로 수식을 1회만 쭉 읽으면서 번역하는 방법이 있고, 다른 하나는 사람이 계산하는 방법처럼 여러 번 읽으면서 다시 쓰기 하는 방법이다. 일단 사람이 하는 방법부터 보자.



단순 무식해서 이해하기 쉬운 다시 쓰기 방법


0단계 : A = B + C * (D + E) 
1단계 : A = B + C * T1 ☞ T1 = D + E 
2단계 : A = B + T2 ☞ T2 = T1 * C 
3단계 : A = T3 ☞ T3 = T2 + B

번역 결과 :
T1 = D + E
T2 = T1 * C
T3 = T2 + B
A = T3

원문에서 계산 순서가 높은 것을 찾아서 그것부터 번역하고 변수 하나로 대체해 가는 방식이다. 이 때 수식을 다른 곳에 다시 써야 한다. 이 방식으로 하면 메모리는 원문의 3배 정도만 필요하다. 번역문이 들어갈 공간 C, 다시 쓰기 할 공간 A, B이다. 다시 쓰기 할 때는 A와 B 사이를 왔다 갔다 하면 된다. 다시 쓰기 하는 시간 낭비는 있지만 단순 명확해서 오류가 나기 힘들다. 또한 한 번만 번역하면 계속 재활용할 수 있다.



고급스럽고 복잡해서 이해하기 어려운 함수 재귀 호출


0단계 : A = B + C * (D + E)
1단계 : A = B +, 여기까지 읽고 A = 는 마지막 처리 위해 기억
2단계 : A = B + C *, 여기까지 읽고 B + 도 나중 처리 위해 기억
3단계 : A = B + C * (, 여기까지 읽고 C * 도 나중에 처리하기 위해 기억
4단계 : A = B + C * (D + E), 여기까지 읽고 T1 = D E + 로 번역하고 상위로
5단계 : A = B + C * T1, 보류했던 것을 T2 = C T1 *로 번역하고 상위로
6단계 : A = B + T2, 보류했던 것을 T3 = B T2 +로 번역하고 상위로

여기 방법은 함수 재귀호출을 이용해 보류했던 내용을 스택에 보관하는 방법이다. 이 방법은 접미사 형태의 번역문을 스택에 보관한다. 이 때 변수 대신 변수의 값을 직접 넣어 두면, 스택에서 빼 오는 순서 그대로 계산하면 된다. 앞의 방법과 번역문의 메모리(스택) 사용 양은 같다. 계산 속도도 같다. 앞의 방법은 번역된 것을 계속 가지고 있고, 이 방법은 계산하는 과정에서 스택의 내용이 변하기 때문에 다신 번역(어순 변경 작업)해야 한다.



1항 연산자, 2항 연산자, 우선순위와 미리 읽기


우리가 아는 가감승제 4칙 연산은 2항 연산자다. 3항 연산자는 없다. 연산자가 좌우 양쪽의 값으로 계산을 한다. 헌데 음수 기호는 1항 연산자다 우측의 부호만 바꾼다. 문제는 부호 바꿈 기호가 빼기 연산자와 같은 모양이란 것이다. (차라리 음수 부호는 다른 걸 쓰는 게 더 간단하다.) 이 경우 연산자 2개가 겹쳐 나오게 되는 것이 힌트다. 괄호 기호도 포함해서 연산자와 괄호가 겹쳐 나오면 궁디(뒤)부터 처리하란 신호다.

원문 : A = B * -C + D * (E + F)
번역 : C~, B *, E F +, D *, +, A =.
해석 : C를 음수로, B와 곱하고, E와 F 더하고, D 곱하고, 앞 2개 결과 더하고, A에 대입하라.

빼기 연산자와 부호 바꾸기 연산자를 구분하기 위해서 ~로 고쳤다. 여기서 읽다 보면 *와 –가 이어 나오고, *와 (가 이어 나온다. 그런 경우 먼저 뒤의 것을 먼저 처리해야 한다. 수학에선 *와 /와 괄호로 묶인 것을 항이라고 한다. 항은 먼저 계산해야 한다. 전통적 수식이 계산 순서대로 배치되어 있지 않기 때문이다. 또한 우선순위가 낮은 +와 – 연산자의 경우 한 단계 더 읽어서 뒤에 우선순위가 높은 *와 /가 나오는지 확인해야 한다. 이를 미리 읽기라 한다.

※ C언어에선 대입 연산자 =를 2항 연산자로 취급한다. 복잡한 수식 안에서 여러 변수에 같은 값을 대입할 수 있다는 장점은 있지만 쓸데없이 복잡하기만 하다. C언어는 좀 허세가 심하다. 목표에 충실한 가장 간단한 방법을 가장 많이 사용한다. 결국 이런 문법을 복잡한 수식 안에서 사용 안 하는 이유는 소스 코드 독해가 어렵기 때문이지. 그래서 오직 초기화 할 때만 사용한다. A = B = C = D = 3.141592.



선언문 = 단어장


C언어를 보면 제일 앞에 변수와 함수 선언문이 나온다. 함수의 경우 내용 정의가 없지만 문법 확인을 할 수 있는 정도의 정보는 나열되어 있다. 

결과 값 리턴 타입, 함수이름, 매개변수(파라미터) 개수와 종류

이렇게 하는 이유는 아래 앞으로 나올 코드들의 문법 확인을 쉽게 하기 위해서다.  미리 단어장을 만들어 두는 것이다. 이렇게 하지 않으면 새로운 변수, 함수를 만날 때마다 단어장에 넣어야 하며, 지금 보고 있는 문장 문법 확인하기 위해서 뒤의 함수 정의 등을 찾아 볼 수는 없기 때문이다. 사람이나 기계나 단어 정의는 먼저 해 두는 것이 편하다.

  • 다음 나열된 단어는 상수이다. 그 불변 값은 이렇다.
  • 다음 나열된 단어는 변수이다. 그 초기 값은 이렇다.
  • 다음 나열된 단어는 함수이다. 매개변수는 이렇다.

단어의 종류, 즉 품사 구분을 해 주는 접두사나 접미사를 단어에 붙여 표현하는 것이 좋다. 왜? 컴퓨터나 사람이나 그 단어를 단어장에서 찾아보지 않고도 문법이나 문맥 검사를 해 볼 수 있기 때문이다. 옛날 Basic 언어에는 이런 접미사가 있다. 프로그래머가 일부러 만들어 붙일 수도 있다. 이런 기능은 코드 쓰기엔 불편하지만 나중에 읽을 때 편하다. 그래서 편집기에서 자동으로 붙여 주는 것이 좋다.

  • 상_원주율 = 3.141592 ☞ 고정 불변
  • 변_반지름 = 100 ☞ 초기 값으로 100을 지정
  • 함_원둘레(반지름) ☞ 반지름만 알면 된다.
  • 함_원면적(반지름) ☞ 반지름만 알면 된다.

※ Apple Mac 컴퓨터에서 사용하는 Objective C라는 언어는 함수 파라미터에도 설명문을 붙일 수 있더라. 그래서 함수처럼 보이지 않던데 코드에 대한 설명을 붙이라고 강요하는 것 같더라.




간단한 형식 문법 체크, 예측 문법


상세한 번역 전에 간단한 형식적 문법 확인부터 먼저 하는 것도 좋다. 전반적인 균형과 틀을 보는 것이다. 이런 형식적 문법 체크 기능은 편집기에 넣으면 좋다. 즉 한 줄 입력하면 바로 문법 확인해서 고치도록 알려 주는 것이 좋다.

  • 단어 철자가 틀렸는지 : 특히 I(아이)와 l(엘)과 1(일), 0(영)과 O(오우) 등
  • 괄호의 짝이 맞는지 : {[(~)]} “~”, ‘~’열린 것은 닫혀야 한다.
  • 구분자가 맞는지 : .,:;!? 앞과 뒤를 구분하며 끝과 시작을 알린다.
  • 파라미터 개수 : 함수는 파라미터(매개변수) 개수가 정해져 있다.
  • 연산자 중첩 : 기호 2개를 붙여 하나의 연산자로 취급할 수 있다.
  • 반복/조건문의 틀 : 괄호의 짝을 맞추는 것과 비슷한 것이다.


이런 문법 확인이 가능한 이유는 예측 문법 때문이다. 앞부분의 형태를 보면 뒤에 어떤 틀이 나올 것인지 예측이 가능하기 때문이다. 고로 그 틀만 맞는지 확인하면 된다.

하나 의아한 것이 있는데 왜 편집기에서 반복문 조건문의 틀을 잡아 주지 않는지 모르겠다. 반복문과 조건문은 괄호와 비슷해서 자기 짝이 있다. 번역할 때는 자기 짝을 확인하기 위해서 눈에 보이지 않지만 키워드에 ID를 붙여야 한다. 프로그래머는 일부러 설명문 형태로 눈에 보이게 ID를 붙이기도 한다.

HTML언어에선 괄호에 ID를 붙이는 방식이다. 시작 괄호엔 /가 없고 끝 괄호엔 /를 넣는다. 괄호의 ID는 <~> 사이에 넣는다. 고로 괄호 <~>는 함부로 사용할 수 없는 기호다. 

<이름> 여기엔 보통 단어나 문장이 들어간다. </이름>

이렇게 하는 이유는 중첩 괄호가 무지 많이 나오기 때문에 자기 짝을 확인해야 할 필요가 있기 때문이다. 그걸 프로그래머가 하라고 이렇게 만든 것인데 컴퓨터가 확인해 주는 것이 더 바람직하다. 이런 기초적인 문법은 실수하지 않도록 컴퓨터가 틀로 제공하고 사람이 못 건드리도록 하는 것이 좋다.

위보다는 <이름: 어쩌구 저쩌구 :이름> 형태가 더 깔끔하지 않나?

반복/조건문에선 조건문 쪽에 애매모호한 문제가 있다. 컴퓨터 언어에선 괄호처럼 짝이 맞거나 구분자처럼 끝과 시작을 확실하게 해 줘야 한다.

if ~ then ~
if ~ then ~ else ~
if ~ then if then ~ else ~ = if ~ then (if then ~) else ~ = if ~ then (if then ~ else ~ )

위와 같은 형태는 종료를 알려 주는 키워드가 없다. 고로 중첩해서 사용할 경우 애매모호한 해석에 빠진다. 즉 이렇게 해석할 수도 있고 저렇게 해석할 수도 있는 경우가 나온다. 그래서 그걸 해결하는 복잡한 번역기를 만들기보다는 다음과 같은 형태로 문법을 바로 잡는 것이 더 간단하다.

if ~ then ~ 끝
if ~ then ~ else ~ 끝
if ~ then (if ~ then ~ 끝) else ~ 끝 ≠ if ~ then (if ~ then ~ else ~ 끝) 끝

※ C언어에선 이 문제를 해결하기 위해 문장 블록을 괄호 {~}로 표현하고, 문장 구분을 ;으로 하고 있다. 즉 문장의 끝과 시작을 알리는 것이 ;이다. 여러 문장들을 묶을 때 {~}로 감싼다. C언어는 수식처럼 괄호의 중첩이다. 그래서 C언어를 배울 때 마치 수식을 보는 듯한 착각을 일으킨다. C언어가 고급 언어가 아니라고 하는 이유 중에 하나이다. 명령문이 마치 수식처럼 보인다. 솔직히 다른 고급 언어 문법에 C언어가 가진 장점만 섞는 게 더 바람직하겠다.



상수, 변수, 함수, 포인터(간접 주소), 배열과 문자열


  • 상수란 항상 일정한 수를 말하기 때문에 프로그램 시작과 끝까지 값이 변하지 않는다. 
  • 변수란 변하는 수를 말하기 때문에 값을 바꿀 수 있다. 고로 변수는 값을 넣을 메모리 공간의 주소로 표현 된다.  
  • 함수란 수를 처리하는 상자란 의미다. 고로 매개 변수를 받아 계산한 후에 결과 값을 주는 코드의 주소로 표현 된다. 매개변수와 결과 값은 스택을 통해 전달한다.
  • 문자열이란 문자들의 1차원 나열이다. 즉 단어나 문장이 된다.
  • 배열이란 같은 구조의 n차원 나열이다.  예를 들어 99단이라고 하면 2차원 배열이다.
  • 포인터란 문자열, 배열, 함수처럼 거대한 내용의 시작 주소이다.


보통 숫자를 다룰 경우는 이 차이를 인식하지 못 하는데 배열과 문자열을 다루게 되면 차이를 느끼게 된다. 

  • 3.141592...는 상수이다.
  • π는 상수를 간단하게 표시한 것이다.
  • 수식에서 보통 a, b, c 등 알파벳으로 표기한 것은 변수이다.
  • sin, cos, tan, exp, log 등은 함수이다.
  • 문자열은 문자의 배열이다.
  • 배열은 수식에서 변수 이름 옆에 붙이는 첨자와 같은 개념이다. a₁, a₂, a₃, a₄


보통의 고급 언어에선 문자열 처리가 쉽지만 C 언어에선 문자열 처리가 어셈블리어처럼 불편하다. 여기선 상수 문자열과 변수 문자열의 차이가 명확해진다. 문자열 변수의 경우 길이가 변하는 문제가 있다. 그건 메모리 공간 할당의 문제를 말한다. 고로 보통 문자열은 힙이라는 곳(단어장이나 연습장 정도?)에 저장하고 변수는 이 주소만 담고 있다. 고로 주소만 살짝 바꾸면 문자열을 직접 지울 필요 없이 지운 것처럼 보인다.

주소1: 홍길동
주소2: 을지문덕
주소3: 김구
변수A = 주소2 = 을지문덕

이렇게 문자열을 가리키는 주소를 담고 있는 변수가 문자열 변수인데 문자열만 가리키지 않고 같은 자료 형의 단순 반복인 배열(아파트 구조), 객체(변수+함수)나 다른 변수까지 가리키게 할 수 있지 않을까? 그런 변수가 포인터 변수이고 간접 주소 방식이라고 한다. 그러니까 A(포인터)를 찾아 가니 B(변수)로 가라고 하고, B(변수)를 찾아가니 C(값)를 알려주더란 것이다.

※ 헌데 C언어의 포인터 표기법은 정말 쓸데없이 복잡한 허세다. 그렇게 복잡하게 만들어도 결국 사람들이 쓰지 않잖아? 소스 코드 독해가 너무 어려우니까. 같은 목적지에 도달하는 더 간단한 방법을 쓰지 왜 복잡한 방법을 쓰겠어? Java 같은 언어는 같은 포인터 선언을 더 쉽게 한다.

함수 호출할 때는 스택(먼저 넣으면 나중에 빼게 되는 구조)에 예를 들어 이렇게 내용을 저장한다. 이건 서로 약속이 되어 있어야 한다.

1단계 : 결과 값을 받을 공간 저장(사용자 작성 프로그램에서 하는 짓) 
2단계 : 파라미터(매개변수)들 저장(사용자 작성 프로그램에서 하는 짓) 
3단계 : CPU/MPU 레지스터 상태 저장 (기계가 자신의 정신 상태를 자동 저장) 
4단계 : 복귀할 주소 (기계가 자동으로 저장) 
5단계 : 함수 실행 코드가 있는 주소로 점프 

항상 돌아올 곳을 알아야 한다. 그래서 점프할 때 기계가 자동 저장을 한다. 호출 당한 함수는 3/4단계 정보는 건드리지 않아야 한다. 자신이 필요한 파라미터의 수와 종류를 알기 때문에 스택에서 읽어낼 수 있다. 계산 결과도 어디에 저장할지 알고 있다. 일을 다 처리한 후에 원상 복귀 명령이 실행되면 3/4단계 정보는 기계가 자동으로 읽어 복구 시킨다. 그럼 호출을 한 쪽에서 2단계 내용을 지우고, 1단계 내용을 받으면 된다.




문맥(같은 표현에 다른 의미) 확인


컴퓨터에서 문맥이라 하면 주로 변수의 종류(Type타입)가 어울리는지 확인하는 것이다. 겉으로 본 형식 문법에는 문제가 없는데 막상 내용을 보니 문자열과 수를 더하라거나(방법은 2가지), 정수와 실수를 더하라거나(방법은 3가지) 하는 문제가 있을 수 있다.

문자열을 숫자로 바꾼 후 더하기 : “123” + 876 = 999
숫자를 문자열로 바꾼 후 붙이기 : “주소” + 123 = “주소123”
정수를 실수로 바꾼 후 더하기 : 123 + 876.999 = 123.000 + 876.999 = 999.999
소수점 이하를 버린 후 더하기 : 123 + 876 = 999
소수점 이하 반올림 후 더하기 : 123 + 877 = 1000

OK 문자열은 이해하겠는데 정수/실수 구분이 있어? 아쉽게도 컴퓨터 진화에선 정수가 먼저 생기고 실수가 나중에 생겼다. 그래서 저장 형식이 서로 다르다. 그러나 정수를 실수로 변환하는 것은 손바닥 뒤집기처럼 쉽다. 정수가 따로 존재하는 이유는 간단한 개수 셀 때 편하기 때문이다. 실수는 공학적 계산을 할 때 필요하다.

정수 : 9876543210
실수 : 9876543210.0123456789

고정 소수점을 취할 경우 정수의 2배 저장 공간을 사용해서 간단하게 실수를 만들 수 있다. 허나 천문학적인 숫자 계산은 할 수가 없기 때문에 부동 소수점 방식을 취한다.
※ 정수, 실수, 바이트, 논리, 문자열 등 기본형 말고 사용자가 조립해서 정의할 수 있는 형이 있다. 이런 것이 발전해서 결국 객체라는 것도 나온다. 여하튼 이런 복잡한 자료 구조를 가리키는 포인터(주소변수)는 당연한 것이 된다.

※ C언어의 포인터 변수 선언은 정말 불필요할 정도로 복잡하다. 참 쓸데없는 짓이다. 자료 구조에 이름을 붙이고 그 이름을 가리키게 하면 되는 간단한 문제인데? 그래서 지금은 포인터 무시하고 Java 이후 객체 지향 언어에선 변수 이름만 가지고 간단하게 쓰고 있지만. 미쳤나? 정력이 남아돌지? 



객체 지향 언어란?


문자열은 문자의 배열이었고, 단순 계산기 시절엔 반복 계산한 결과를 배열에 저장했다. 헌데 서로 다른 종류의 데이터 형을 묶을 필요가 생겼다. 그게 사용자 정의 자료 형이다.

컴퓨터가 단순히 계산기 수준을 넘어서고, 문자열을 다루면서 타자기를 대신하게 된 후에, 전쟁 시뮬레이션이나 컴퓨터 게임을 만들게 되면서 객체란 개념이 나타난다. 여기서 객체란 병사, 전차, 전투기, 미사일 같은 유닛을 말한다. 같은 설계로 만들어지지만 각 객체는 서로 다른 위치, 상태에 있다. 

  • 최대 이동 속도 : 엔진의 힘이라고 보면 된다.
  • 최대 이동 거리 : 연료의 양이라고 보면 된다.
  • 장갑 두께 vs 무게 : 방어력과 관성에 영향
  • 유효 사거리 : 거리에 따라 정확도도 떨어진다.
  • 연사 속도 : 분당 발사 수
  • 무기 종류 : 총알, 포탄, 미사일
  • 현재 위치, 현재 속도(방향 포함), 현재 가속도(방향 포함)

바로 이런 객체 설계도가 사용자 정의 자료 구조였고, 각 객체는 이 자료 구조를 배열로 만들면 되는 거였다. 고로 기존의 고급 언어나 C언어로도 객체 지향 프로그램을 할 수 있다. 객체 지향 언어란 객체 지향 프로그램을 강요한 것을 말한다. 아마 접두사/접미사를 붙이며 프로그래밍을 하던 프로그래머가 언어 문법으로 만들어 강요하게 되었을 것이다.

보통 객체 지향 언어는 특정 자료 구조에 특정 함수들을 연결하도록 강요한다. 함수들은 그 함수가 다루는 자료 구조가 정해져 있다. 헌데 아주 많은 함수들이 이곳저곳에 흩어져 있을 때 그 함수와 자료 구조를 연결하여 해석하기 힘들다. 그래서 서로 짝이 맞는 함수와 자료 구조를 함께 적어 놓도록 강요한 것이 객체 지향 언어이다. 별거 아니지?

이렇게 식구들을 모아 가족을 만들었으면 성을 붙여 줘야 할 거 아닌가? 이 변수들과 저 함수들은 모두 그 객체에 속하니 객체 명칭을 성姓으로 쓰는 것이다. 변수와 함수를 모아서 정의 해야 하고 사용할 때도 성씨를 붙여 줘서 구분해야 하는 것이다. 별거 아니지?

  • 전차.속도 = 40km/h
  • 전차.위치 = (X, Y, Z)
  • 전차.가속도 = 0
  • 전차.장갑 = 30mm
  • 전차.사거리 = 3km
  • 전차.연사속도 = 분당 12발
  • 전차.발사체 = 포탄

※ 개체 지향 언어에서 보이는 가장 얼빵한 짓을 예로 든다면 수학 함수, 문자열 함수, 날짜 시간 함수 같은 무소속이 당연한 전역 함수를 객체에 포함시킨 것이다. 이건 객체 지향 목적에도 맞지 않고 불편하기만 하다. 또한 단 하나의 값만 들어가는 기본 자료 형도 객체로 만든 것이다. 객체란 여러 자료 형이 섞여 있는 구조일 때 의미가 있는 것인데 정말 얼빵한 짓이다.

이렇게 하는 이유는 소스 코드 해석 중에 그 자료 구조와 상관있는 함수를 쉽게 파악할 수 있기 때문이다. 그 자료 구조 바로 밑에 함수들을 적어 놓기 때문이다. 객체 지향 언어는 객체 지향 프로그램을 돕는 게 아니라 강요하는 것이다. 

객체 지향 프로그램을 돕는 것은 오히려 막강한 소스 코드 편집기다. 편집 중에 객체와 함수의 이름들을 다 기억하고 있다가 알려 주는 편집기야 말로 객체 지향 프로그램을 돕는 놈이다. 이게 없다면 객체, 함수, 변수 이름을 따로 정리해 놓고 있다가 직접 검색 확인해서 입력해야 한다.

헌데 막상 소스 코드를 해석해 보면 함수의 호출 관계를 보여주는 조직도 비슷한 것이 필요하다. 이런 것을 해 주는 프로그램이나 편집기는 아직 없더라. A함수를 해석하는데 의미를 알 수 없는 B함수를 호출하더라. B를 찾아가 보니 또 C를 호출하는데 C를 찾아가면 D를 호출하더라. 뭐 이런 식으로 가니 소스 코드 해석이 어렵더라. 객체도 마찬가지. 이 객체가 다른 객체를 포함하고 있어 찾아가면 또 다른 객체를 포함하고 있고 뭐 그런 식이다.





2017년 4월 5일 수요일

한국의 IT를 육성하는 아주 좋은 방법

상상은 자유다마음대로 상상하자.

우리가 농업의 가격 경쟁력이 떨어져도 생산량을 유지하는 이유는 전쟁 때문이다마찬가지로 전쟁 무기에 사용되는 기술은 경제성이 떨어져도 적정 생산량과 기술을 보유하고 있어야한다. IT 기술도 그러한 것들 중에 하나다.

 솔직히 왜 미국유럽러시아가 다른 대륙의 후진국들을 정복하지 않는지 잘 모르겠다다른 후진국들은 무기도 제대로 못 만드는 나라들인데 말이지아마도 강대국끼리의 경쟁과 전쟁이 두려워서겠지. 미국은 식민지가 없고, 러시아는 거의 불모지인데 유럽은 식민지가 많았으니까.

IT 기술의 경제성이 떨어지는 이유는 불법 복제 때문인데 이걸 막을 방법은 거의 없다요즘은 서버에 연결을 해야 사용할 수 있도록 하던데 아직도 네트워크가 부족한 나라가 많으니 정답은 아니다그러니 차라리 자유 복제를 허용하고 다운로드/복제 수에 따라 인기도를 측정해서 국가 차원의 보상을 하자. IT 세를 걷는 것이다.

소스 코드는 국가 서버에 특허처럼 등록해서 기술 발전에 도움이 되도록 하는 것이다소스 코드를 공개해야 하기 때문에 남의 코드를 사용했다면 숨기기 힘들다특허료처럼 약간의 지불을 해야 한다기술 독점을 허용하지 않는 대신 자동으로 보상은 해 주는 것이다오픈 소스 운동도 흡수하고 일거양득이다.

※ 짧은 특허 독점 기간 때문에 독점권은 사실 무용지물이다차라리 일단 아무나 사용하게 해 주고 후불로 보상 받는 것이 낫다사용료는 국가에서 정한 규칙에 따라 결정하면 된다쓸 데 없이 가격 협상하느라 시간 낭비할 필요가 없다돈을 제공한 자와 기술을 제공한 자 모두 배당금과 같이 50%씩 이익을 챙긴다저작권처럼 특허 사용료는 저렴한 대신 특허 기간은 길게 (평생해 주는 것이 영세 기술 개발자에게 낫다.



그 사회의 정치를 판단하는 손쉬운 최종 평가 기준은?
평균 수명 후진국일수록 수명이 짧다
빈부 격차 후진국일수록 격차가 크다

2017년 1월 23일 월요일

컴퓨터 부동 소수점, 고정 소수점 이해, Visual Basic Macro에서 사용하는 데이터 형식

2024-02-14-수 수정

수학에서 말하는 수의 종류와 컴퓨터에서 표현법이다.

  • 자연수 : 0 이상의 양의 정수, 예) 0, 1, 2, 3...
  • 정수 : 소수가 없는 정수, 예) –3, -2, -1, 0, 1, 2, 3
  • 소수 : 0.XXX로 1보다 작은 수, 예) 0.123, 0.01%
  • 실수 : 정수 + 소수, 예) 123.456, 3.141592
  • 고정 소수점 표현 : 예) 정수부 3자리 + 소수부 3자리 = 123.456
  • 부동 소수점 표현 : 예) 가수부 3자리 x 지수부 3자리 = 0.123456×10¹²³

숫자를 표현할 수 있는 공간(칸)이 6개라고 하자. 여기서 고정 소수점이란 소수점의 위치가 고정 되어 있음을 뜻한다. 소수점 위 3자리, 소수점 아래 3자리처럼 말이다. 부동 소수점이란 소수점 위치가 가변적이란 얘기다. 부동浮動float플로우트는 떠다님을 의미한다. 다른 말로 지수 형태 표현이라고 할 수 있다. 소수점의 위치는 지수指數가 결정하고, 수의 정밀도는 가수가 결정한다. 가수假數란 가짜 수란 의미인데 소수점 이하를 의미한다.

컴퓨터 메모리를 너무 어렵게 생각하지 말자. 우리가 초등학교 때 사용하던 원고지처럼 바둑판 격자가 그려진 공책을 상상하면 된다. 각 칸에는 글자 하나 적을 수 있다. 숫자를 적을 공간에 한계가 있는 것은 당연하다. 컴퓨터 메모리도 그러하다. 한 바이트라고 하는 것을 알파벳 문자가 들어가는 자리라고 생각하라. 

예를 들면 분해능 1000의 저울이 있다고 하자. 1g단위로 1kg까지 측정하는 저울이 있고, 1kg단위로 1톤까지 측정하는 저울이 있다. 여기서 저울 눈금에 표시되는 숫자는 가수에 해당한다. 눈금 간격이 정밀도(분해능)를 나타낸다. 그 뒤의 단위(g/kg)는 지수에 해당한다. 이건 규모(크기)를 나타낸다. 이 정도 설명해도 이해 못 하면 국정원 댓글 용역들이다.

그런데 기억 공간이란 것은 한계가 있다. 공간(칸수) 제약이 있는 숫자 표시를 할 때는 2가지 개념이 있다. 측정기나 컴퓨터 속의 수를 너무 어렵게 생각하지 말자. 중앙이 0이고 좌우가 ±30㎝인 자를 상상하자. 이 자의 최소 눈금은 1㎜이다. 그럼 이렇게 정리 할 수 있다.

  • 범위 : ±300㎜ → 크기
  • 눈금 : ±1㎜ → 정밀도/분해능

범위는 얼마나 큰 수, 작은 수를 표현할 수 있느냐이고, 눈금은 얼마나 섬세하게 나누어 표현할 수 있느냐이다. 이런 숫자 표현을 컴퓨터에선 비트 나열로 어떻게 할까? 그게 궁금하다. 2진수와 10진수를 바꾸는 개념이 필요하다. 몇 개의 숫자는 기억하자.


  • 1바이트(08비트) : 256개 정수 표현
  • 2바이트(16비트) : 6만5536개 정수 표현
  • 4바이트(32비트) : 42억9496만7296개 정수 표현
  • 8바이트(64비트) : 184경4674조4073억7095만51616개 정수 표현


Visual Basic Macro에서 사용하는 데이터 형식


컴퓨터에서 하나의 데이터 표현에 사용하는 기억 공간은 현재 8바이트(64비트)까지이다. 1바이트(8비트)에서 시작해서 2배씩 증가하는 것이 전통이다. 주어진 바이트 공간이 같아도 내용을 어떻게 해석하느냐의 차이가 있어 Data Type(자료 종류)이라 부른다.

  • 1바이트 : byte(자연수)
  • 2바이트 : integer(정수), boolean(논리)
  • 4바이트 : long(정수), single(실수)
  • 8바이트 : longlong(정수), double(실수), currency(화폐), date(시간)


대부분 컴퓨터 언어에선 전통적인 숫자 표현이다. 정수의 경우는 이해하기 쉽다. 고로 실수를 어떻게 표현하는지 분석해 볼 것이다. single, double, date는 부동 소수이고, currency가 고정 소수이다. 기타 흥미로운 자료형도 있다.

  • 14바이트 : decimal(10진수)
  • 16바이트 : variant(가변적)


decimal은 10진수를 표현한 것이다. 이건 단독 사용이 안 되고 variant와 함께 사용해서 다른 수를 10진수 형식으로 바꾼다. variant는 여러 형태의 데이터를 다 표현할 수 있도록 넓힌 공간이라 가장 많은 바이트를 차지한다. 추가된 2바이트는 자료 형식에 대한 정보이다. 고로 10진수 표현이 어떠한지 분석해 볼 것이다.

기타 object(객체), longptr(포인터) 등은 메모리 주소(위치)와 데이터 구조를 나타내는 자연수로 사람이 직접 건드릴 일이 없다. 데이터 구조란 여러 종류의 자료형을 묶어서 한 덩어리로 취급할 때 필요한 것이다. 고로 위치/크기/종류를 모두 구분해야 한다. 주소의 바이트 크기도 시스템(CPU + O/S)에 따라 자동으로 바뀌니까 신경 쓸 일 없다. 이와 유사한 자료 형식이 있다.

  • string(고정) : 문자열 길이(약 6만까지)
  • string(가변) : 10바이트 + 문자열 길이(약 20억까지)
  • variant(문자열) : 22바이트 + 문자열 길이(약 20억까지)


길이가 가변적인 자료형의 대표적이고 전통적인 것이 문자열이다. 길이가 고정된 문자열은 보통 단어 목록(사전)을 표현할 때 사용한다. 최대 한계 길이(약 6만까지 가능)를 정해서 사용한다. 단어 길이가 6만(65536자)이면 한글 250자 원고 (10행x25자) 240 페이지 분량, 500자 원고 (25행x20자) 120 페이지 노트 정도 된다. 여하튼 최대 단어 길이를 정하고 미리 공간을 확보하는 것이다. 이게 말이 단어지 이 정도면 문서 하나 정도가 들어간다.

길이가 가변적인 것은 보통 문장(문단)을 표현할 때 사용한다. 메모리는 절약 되지만 구현이 복잡하다. 문자열은 바이트의 배열이 된다. 고로 문자열 변수는 이 배열의 주소(위치)를 가리키게 되면서 문자열 길이도 표현해야 한다. 고로 4바이트(약 20억)는 길이, 나머지 4바이트는 주소(위치)를 표현하는 것 같은데 남은 2바이트는 뭔지 모르겠으나 컴퓨터가 알아서 할 일이다.

데이터를 비트로 표현하는 방법을 분석하기 위해 사용할 프로그램이다.


2진 부동 소수점 형식 분석


10진수로 된 실수를 2진수로 바꾸는 것은 정확하지 않고, 계산도 복잡하다. 고로 가수, 지수, 부호 비트를 구분해서 분석하려면 대가리를 좀 굴려야 한다. 2진수는 m×2ⁿ 형태이고 m(가수), n(지수)는 부호가 있다. n과 m의 변화를 보기 위해서 다음과 같은 수를 저장해 본 후에 분석한다.


m
n
10진양수
10진음수
1
0
1
-1
1
1
2
-2
1
2
4
-4
1
3
8
-8
1
4
16
-16
1
5
32
-32
1
6
64
-64
1
7
128
-128
1
8
256
-256
1
9
512
-512
1
-1
0.5
-0.5
1
-2
0.25
-0.25
1
-3
0.125
-0.125
1
-4
0.0625
-0.0625
1
-5
0.03125
-0.03125
1
-6
0.015625
-0.015625
1
-7
0.0078125
-0.0078125
1
-8
0.00390625
-0.00390625
1
-9
0.001953125
-0.001953125
0.1
0
0.5
-0.5
0.11
0
0.75
-0.75
0.111
0
0.875
-0.875
0.1111
0
0.9375
-0.9375
0.11111
0
0.96875
-0.96875
0.111111
0
0.984375
-0.984375
0.1111111
0
0.9921875
-0.9921875
0.11111111
0
0.99609375
-0.99609375
11
0
3 -3
111
0
7 -7
1111
 0
15 -15
11111
0
31 -31
111111
0
63 -63
1111111
0
127 -127
11111111
0
255 -255


간단한 비교 분석을 통해 다음과 같은 결론에 도달한다. 첫 비트는 부호, 그 다음은 지수, 그 다음은 1이 생략된 가수이다. 바이트의 배치 순서는 CPU에 따라 다른데 PC에서 사용하는 인텔 CPU는 하위 바이트를 먼저 기록한다. 그래서 거꾸로 배치해야 사람이 읽기 편하다. 지수는 비트 1자리만 늘어도 2배로 커지는 효과가 있다. 가수는 비트가 2배로 늘어야 2배 정밀도가 된다.


  • 32비트 실수 최소 눈금 : ±1.401298E-45 (2진수이기 때문에 이렇다)
  • 64비트 실수 최소 눈금 : ±4.94065645841247E-324 (2진수이기 때문에 이렇다)

최소 값일 때는 비트 모양이 지수 = 0, 가수 = 000...0001 꼴이다.








날짜와 시간 표현은 64비트 부동 소수점 형식을 빌려 사용하고 있다. 초→분→시→일→월→년으로 1초 시간에 해당하는 소수를 계속 누적하는 형태이다. 1일은 정수 1에 해당한다. 고로 1일을 60초 * 60분 * 24시 = 86400초로 나누면 단위 시간 1초에 해당하는 소수를 구할 수 있다. 365일은 평년, 366일은 윤년이다.


  • 최소 눈금 : 1초 =  0.0000115740740740740... (740무한 반복)
  • 최대 범위 : 1900년 1월 0일 자정(0시0분0초) ~ 약 9999년(1만년)까지






10진 고정 소수점 형식 분석


돈 계산할 때 사용하는 Currency 고정 소수점 형식은 정수와 다를 것 없다. 본질적으로 정수이다. 정수처럼 첫 비트가 부호이다. 양수=0, 음수=1이며 음수는 정수처럼 보수를 취한다. 10진수로 소수점 아래 4자리까지 표현하고 싶다면 10000에 해당하는 수를 1로 보는 방식이다. 다시 말하면 1만 배 곱하여 저장, 계산한 후에 10진수로 결과 표시할 때만 소수 4자리 앞에 살짝 소수점을 찍어 준다. 고로 비트 패턴으로는 정수와 소수의 구분이 없고 그냥 1만배가 된 정수이다.

  • 최소눈금 : ±0.0001 (10진수라서 최소 눈금을 1로 표현 가능)
  • 최대범위 : 약 ±2의 63승 ÷ 10000






10진 부동 소수점 형식 분석


Decicmal의 형식을 읽을 수는 없었으나 10진법이라는 것도 Currency처럼 최소 눈금을 정하고 정수처럼 취급하는 방법으로 보인다. 왜냐하면 최소 눈금은 10진수로 표현했을 때만 1로 끝나기 때문이다. 또한 최대 범위를 보면 약 12바이트가 가수로 사용된다. 고로 남은 2바이트가 부호와 지수가 될 것이다. 헌데 지수가 있다는 말은 부동 소수란 말이다.


  • 최소 눈금 : ±0.0000000000000000000000000001, 10진수 29자리 표시
  • 최대 범위 : 약 ±2의 96승(12바이트), 10진수 29자리 표시


이 방법은 가수는 일반 정수처럼 취급하고, 지수는 10진수로 보는 방식이다. 계산은 내부적으로 어떤 방식으로 알아서 할 것이고, 화면에 표시할 때만 소수점 위치를 살짝 결정해 찍어주는 방법이다. 이렇게 하는 이유는 계산의 정확도를 위함이다. 10진 소수를 2진 소수로 정확히 바꿀 수는 없다. 허나 정수에선 가능하다. 고로 2진수 1을 10진수 최소 눈금으로 잡은 것이다.

이 방법으로도 곱하기 나누기에선 문제가 없다. 가수는 서로 곱하고 나누고, 지수는 서로 더하고 빼면 된다. 문제는 더하기 빼기이다. 지수가 큰 쪽으로 자리수를 맞추어야 하는데 가수는 2진 정수인데 지수는 10진수를 표시하니 복잡하다. 뭐 알아서 잘 했겠지.

이제 정수로 실수를 표현하는 원리를 알았으니 직접 구현할 수도 있을 것이다. 대충 이렇게 마무리한다.






독재정부가 하는 짓은 동서고금 똑같다.
전쟁터에서 하는 짓은 동서고금 똑같다.
한 나라의 역사만 제대로 알아도 나머지 나라 역사를 안다. 외국사 = 한국사
그러니 독재자 이승만, 박정희, 김일성, 한국 전쟁에 대한 환상을 깨자.
우리가 학교에서 배운 한국사는 1920년부터 일본이 가르친 일제 식민 사학이다.
근현대사 또한 친일파 세력 때문에 제대로 교육 받았다고 할 수 없다.

2017년 1월 21일 토요일

Excel(엑셀)로 File Debugger(파일 디버거) 만들기 & 유니코드 파일 분석

옛날 DOS 시절에 사용하던 디버거 프로그램이 없어서 Excel로 만들었다. CPU, O/S, 컴퓨터 언어가 진화하면서 옛날 기계어(어셈블리어)를 사용할 필요가 없어졌다. 고로 CPU 명령어 해석은 불필요하니 파일 데이터만 분석하는 기능만 만들면 된다. Excel의 편리한 인터페이스와 계산 능력을 사용할 수 있어 훨씬 좋은 디버거를 만들 수 있다.





위 화면은 파일의 일부를 바이트 단위로 읽어 여러 방법으로 해석해서 보여주는 화면이다. 옛날 디버거를 사용해 보고 필요하겠다싶은 해석을 넣었다. 일부 바이트를 수정한 후에 써넣을 수도 있다. 그러니 원본 파일을 이름을 바꿔 보전한 후에 해킹을 해야 한다.

8비트 정수를 10진수, 16진수, 2진수로 나타낸다. 16진수는 디버거에서 많이 보는 형태이나 별로 쓸모가 없다. 때론 2진수 패턴을 분석할 필요가 많다. 그리고 16비트로 묶어 정수로 볼 필요도 있다. 문자열이 섞인 경우를 보기 위해 7비트 아스키 문자, 16비트 한글 완성형 문자로 묶어 표현한다. 먼저 기록된 바이트를 상위 바이트로 보는 것은 완성형 코드이고, 하위 바이트로 보는 것은 16비트 정수일 때다.

파일에 따라 때론 32비트 정수, 유니코드 문자 등으로 해석할 필요가 있는데 원리가 같으니 직접 우측 공백에 함수를 이용해 만들어 넣으면 된다. 이런 다양한 해석은 Excel 함수를 이용해서 직접 만들어 넣으면 된다. 





위 화면은 2개 파일의 헤더부분을 비교할 때 사용하는 화면이다. 같은 종류의 파일은 헤더(머리) 부분의 패턴이 거의 같다. 고로 기준이 되는 파일과 내용에 있어 약간의 차이가 나는 비교 파일에서 그 차이 나는 부분만 찾아 표시해 줘서 파일 구조 해석에 도움을 준다.

위와 같이 화면을 만든 후에 다음 코드를 모듈에 복사해 넣어 버튼에 연결시킨다. 화면에서와 같이 정확한 행렬(가로/세로) 위치에 만들어야한다. 그림에서와 같은 Test 파일을 만들어 동작 시험을 해 본다.


Public Sub ReadFile()
    '주소, 값 영역 지우기
    Range("A5:B5").Select
    Range(Selection, Selection.End(xlDown)).Select
    Selection.ClearContents
    '파일 이름, 주소, 읽을 수량 지정
    Filename = Worksheets("디버거").Cells(1, 4).Value
    ByteAddress = Worksheets("디버거").Cells(2, 4).Value
    ByteCount = Worksheets("디버거").Cells(3, 4).Value
    Dim ByteValue As Byte
    '내용 읽어서 배치, EOF보다는 LOF를 쓰는 것이 깔끔하다.
    Open Filename For Binary As 1
        For i = 0 To ByteCount - 1
            If ByteAddress + i > LOF(1) Then Exit For
            Get #1, ByteAddress + i, ByteValue
            Worksheets("디버거").Cells(5 + i, 1).Value = ByteAddress + i
            Worksheets("디버거").Cells(5 + i, 2).Value = ByteValue
        Next i
    Close 1
    '다음 주소 자동 계산
    Worksheets("디버거").Cells(2, 4).Value = ByteAddress + ByteCount
End Sub


Public Sub WriteFile()
    Filename = Worksheets("디버거").Cells(1, 4).Value
    Dim ByteValue As Byte
    Open Filename For Binary As 1
        i = 0
        ByteAddress = Worksheets("디버거").Cells(5 + i, 1).Value
        Do While ByteAddress <> ""
            ByteValue = Worksheets("디버거").Cells(5 + i, 2).Value
            Put #1, ByteAddress, ByteValue
            i = i + 1
            ByteAddress = Worksheets("디버거").Cells(5 + i, 1).Value
        Loop
    Close 1
End Sub


Public Sub CompareFile()
    '주소, 값 영역 지우기
    Range("A3:B3").Select
    Range(Selection, Selection.End(xlDown)).Select
    Selection.ClearContents
    Range("I3:J3").Select
    Range(Selection, Selection.End(xlDown)).Select
    Selection.ClearContents
    '파일 이름 지정
    FileName1 = Worksheets("비교").Cells(1, 4).Value
    FileName2 = Worksheets("비교").Cells(1, 12).Value
    '파일 길이 비교
    Dim ByteValue1 As Byte
    Dim ByteValue2 As Byte
    Open FileName1 For Binary As 1
    Open FileName2 For Binary As 2
    filelength1 = LOF(1)
    filelength2 = LOF(2)
    MaxLength = filelength1
    minlength = filelength2
    If filelength1 < filelength2 Then
        MaxLength = filelength2
        minlength = filelength1
    End If
    '헤더 부분 비교 차이 나는 부분만 표시
    r = 3
    For i = 1 To minlength
        Get #1, i, ByteValue1
        Get #2, i, ByteValue2
        If ByteValue1 <> ByteValue2 Then
            Worksheets("비교").Cells(r, 1).Value = i
            Worksheets("비교").Cells(r, 2).Value = ByteValue1
            Worksheets("비교").Cells(r, 9).Value = i
            Worksheets("비교").Cells(r, 10).Value = ByteValue2
            r = r + 1
        End If
    Next i
    For i = minlength + 1 To MaxLength
        If filelength1 = MaxLength Then
            Get #1, i, ByteValue1
            Worksheets("비교").Cells(r, 1).Value = i
            Worksheets("비교").Cells(r, 2).Value = ByteValue1
        End If
        If filelength2 = MaxLength Then
            Get #2, i, ByteValue2
            Worksheets("비교").Cells(r, 9).Value = i
            Worksheets("비교").Cells(r, 10).Value = ByteValue2
        End If
        r = r + 1
    Next i
    Close
End Sub


이걸 왜 만들었냐고? 옛날 게임 데이터 파일 해킹하려고. 요즘 것은 암호화 되어 있다.

자 이제 어떻게 사용하는지 보도록 하자. 사용법을 알면 감이 잡힐 것이다. 노트패드에서 숫자, 알파벳, 한글의 시작과 끝 문자를 입력해 넣고 4가지 표준으로 저장한다.


  1. ANSI(기존표준) = 영문 아스키 + 한글 완성형(한글 부족) + MS CP949(한글 보충)
  2. 유니코드(UTF-8) = 통신 목적으로 사용하며 기존 영문 아스키와 호환을 유지한다.
  3. 유니코드(UTF-16 little Endian) = 16비트로 세계 문자 표현. 하위 바이트 먼저 기록.
  4. 유니코드(UTF-16 Big Endian) = 16비트로 세계 문자 표현. 상위 바이트 먼저 기록.


※ Endian(엔디안)은 여러 바이트가 하나의 의미를 나타낼 때 바이트 저장 순서를 말한다. 예를 들어 123(백이십삼)을 저장(기록)할 때 기계에 따라 1, 2, 3 순서와 3, 2, 1 순서로 저장해서 구분이 필요한 것이다. IBM PC의 Intel CPU는 3, 2, 1 순서로 기록한다. 옛날 Mac PC 등 다른 컴퓨터 CPU는 1, 2, 3 순서로 기록할 수도 있다.





기존 파일 형식에선 문자 파일의 경우 아무런 예고도 없이 내용이 나온다. 왜냐하면 파일 확장자에 *.txt로 문자 파일임을 나타내기 때문이다. 계산해 보면 0~9, a~z, A~Z까지 숫자가 순서대로 할당되어 있다. 한글 완성형의 경우는 2바이트를 묶어 해석해야 한다. 이 때 상위/하위 바이트를 앞에 것으로 하느냐 뒤의 것으로 하느냐 정해야 하는데 문자의 경우 먼저 나온 것을 보고 뒤의 것을 해석하기 때문에 앞의 것을 상위 바이트로 본다. 16비트 정수 해석은 여기선 필요 없으니 무시한다.

※ 완성형의 경우 시작과 끝 글자의 순서가 뒤죽박죽인데 띨띨하게 완성형 만들 때 모든 한글을 반영하지 않았다. 약 2천 개만 등록했던 것이다. 그래서 MS사(社)에서 불쌍하다며 CP949란 보충 코드를 만들어 주다보니 이렇게 되었다.

유니코드 중에 UTF-8은 기존의 ANSI 문서 파일이 아님을 나타내기 위해서 첫 3바이트가 헤더 형태로 나온다. 그 다음은 아스키와 호환성을 유지하니 같다. 한글 코드가 3바이트로 할당 되어 있는데 이건 가변길이 코드이다. 1번 바이트의 특징을 보고 2번 바이트를 이어 해석하고, 2번째 바이트를 보고 3번 바이트를 이어 해석하는 방식이다. 그러다 보니 한글이 3바이트나 차지하게 되었다. 이건 유니코드를 공부해야 해석이 가능하다. 비트 패턴을 보고 해석해야 한다.

유니코드 중에 UTF-16 Little Endian 순서가 PC에서 표준인 것 같다. PC에선 16비트 정수의 경우 하위 바이트를 먼저 저장한다. 고로 문자 코드는 16비트 정수 해석과 정확히 일치한다. 기존 ANSI 파일이 아니며 UTF-16 Little Endian이란 헤더가 나온다. UTF-16 Big Endian과 정확히 반대 순서이다. 아스키(ASCII) 코드는 8비트에서 상위 바이트에 0을 더해 16비트로 그대로 확장했고, 한글의 경우 16비트 정수 해석 값이 그대로 문자 코드가 된다.

여기서 UTF-16 Big Endian과 UTF-8 가변 길이 코드를 비교해 보자. Big Endian은 우리가 숫자를 나열하는 방식과 같다. 좌측에 먼저 적는 숫자가 높은 자릿수이다. 낮은 자릿수는 가장 늦게 우측에 적는다. 사람이 기록하는 순서와 컴퓨터가 기록하는 순서가 같다. 이 방식이 인간에겐 더 자연스러운 순서이다.

가 = 1010 1100 - 0000 0000 = 1110 1010 - 1011 0000 - 1000 0000
힣 = 1101 0111 - 1010 0011 = 1110 1101 - 1001 1110 - 1010 0011

이제 알겠는가? UTF-8은 비트의 앞을 보고 뒤를 이어 해석하는 방식이다. 비트의 나머지 부분을 묶어 연결해서 UTF-16으로 만들어서 문자처리를 한다. 왜 이 짓을 하느냐? 아스키 코드와 호환성을 위해서다. 아스키 코드엔 옛날 구식 기계 동작을 제어하는 코드가 포함 되어 있어 그 코드를 피하기 위함이다. 일단 UTF-16으로 바꾸면 아주 쉽게 간단한 계산으로 한글의 초성, 중성, 종성을 분리해 낼 수 있다. 완성형은 이게 불가능하다.

첫 바이트 1110은 총 3바이트란 의미다. 1이 3번 나온다. 0은 그냥 구분용이다. 같은 방식으로 110일 경우는 총 2바이트, 11110일 경우는 총 4바이트란 의미다. 뒤에 이어지는 바이트 모두 10으로 시작한다. 만약 첫 비트가 0이면 그건 아스키 코드와 같다는 뜻이다.

대충 사용법은 알겠지? 16비트 정수 해석 순서를 바꾸려면 빈 셀에 계산식만 추가로 더 입력해 넣으면 된다.








인간처럼 똥 많이 싸고 쓰레기 많이 버리는 동물도 없을 거다.
불필요하게 많이 먹고, 불필요하게 많이 생산한다.
살기 위해 돈 벌어야 하고, 돈 벌려고 쓰레기를 만들어야 한다.
미래엔 진짜 1명만 일하고 9명이 백수인 세상이 올 것이다.
로봇(기계+컴퓨터)에 의한 복지국가 외엔 답이 없을 것이다.

2017년 1월 20일 금요일

ASCII(아스키) 코드 이해하기

미국정보교환표준부호(American Standard Code for Information Interchange; ASCII) : 1967년 표준 제정, 1986년 마지막 개정

이 코드를 이해하려면 먼저 타자기 시대를 이해해야 한다. 년도를 보면 타자기를 사용하던 시절에 제정하여, 컴퓨터 화면 출력 시절에 마지막 개정을 한다. 타자기인데 사람이 치는 것이 아니라 기계가 통신을 해서 자동으로 치는 타자기이다. 그래서 제어 명령(통신에 필요한 명령 + 타자기를 움직이는 명령)이 함께 들어 있다. 컴퓨터와 프린터도 이 아스키 코드로 통신할 수 있다. 프린터는 자동 타자기에 해당한다.


이진법
십육진법
십진법
약자
설명이 아닌 이름
엉터리 설명에 대한 추측
000 0000
00
0
NUL
Null Character
공백 문자(문자가 아니라 그냥 없음 표시)
000 0001
01
1
SOH
Start of Header
헤더 시작
000 0010
02
2
STX
Start of Text
본문 시작 & 헤더 종료
000 0011
03
3
ETX
End of Text
본문 종료
000 0100
04
4
EOT
End of Transmission
전송 종료 & 통신 초기화
000 0101
05
5
ENQ
Enquiry
응답하라!
000 0110
06
6
ACK
Acknowledgment
긍정응답 = OK/Yes/그렇다!
000 0111
07
7
BEL
Bell
경고음(삐~)
000 1000
08
8
BS
Backspace
백스페이스 = 커서 좌측 이동
000 1001
09
9
HT
Horizontal Tab
수평 탭 = 커서 우측 몇 칸 이동
000 1010
0A
10
LF
Line feed
개행 = 종이 한 줄 정도 올리기
000 1011
0B
11
VT
Vertical Tab
수직 탭 = 커서 아래 몇 칸 이동
000 1100
0C
12
FF
Form feed
다음 페이지 = 종이 넘기기
000 1101
0D
13
CR
Carriage return
복귀 = 커서 좌측 끝으로 이동
000 1110
0E
14
SO
Shift Out
확장문자(외국문자/그림문자) 시작
000 1111
0F
15
SI
Shift In
확장문자(외국문자/그림문자) 종료
001 0000
10
16
DLE
Data Link Escape
전송 제어 확장(탈출? 다른 방식으로 통신?)
001 0001
11
17
DC1
Device Control 1
장치 제어 1 (다른 장치가 있다면)
001 0010
12
18
DC2
Device Control 2
장치 제어 2
001 0011
13
19
DC3
Device Control 3
장치 제어 3
001 0100
14
20
DC4
Device Control 4
장치 제어 4
001 0101
15
21
NAK
Negative Acknowledgement
부정응답 = NG/No/아니다!
001 0110
16
22
SYN
Synchronous idle
동기 = 상대와 대화하기 위해 기다림?
001 0111
17
23
ETB
End of Transmission Block
전송블록 종료
001 1000
18
24
CAN
Cancel
무시 = 방금 전송한 것 무시해라?
001 1001
19
25
EM
End of Medium
매체 종료(중간 종료?)
001 1010
1A
26
SUB
Substitute
치환 = 잘 못 전송한 것 대체?
001 1011
1B
27
ESC
Escape
제어기능 추가(탈출? 다른 제어 명령 사용?)
001 1100
1C
28
FS
File Separator
파일경계 할당 = 파일 구분자
001 1101
1D
29
GS
Group Separator
레코드 그룹 경계 할당 = 그룹 구분자
001 1110
1E
30
RS
Record Separator
레코드 경계 할당 = 레코드 구분자
001 1111
1F
31
US
Unit Separator
장치 경계 할당(단위 구분자?)
이 구간은 화면에 표시 가능한 문자
알파벳 + 숫자 + 기호
111 1111
7F
127
DEL
Delete
삭제 = 잘 못 전송한 것 삭제?


위에서 보통 Null이라고 하면 정말 아무 것도 없는 것을 말한다. 즉, 숫자 0도 아니고 문자 공백도 아니라 정말 무(無)이다. 

1~6은 통신에 관한 것이다. 통신 시작한다. 끝낸다. 응답하라. 그런 내용이다.

7~15은 타자기를 제어하는 명령이다. 소리를 내거나. 문자를 두드리는 타자기 머리(케리지, 컴퓨터에선 커서)와 종이 넘기는 바퀴를 제어하는 것이다. 머리를 좌우로 한 칸 움직이거나 종이를 한 줄, 한 페이지 넘기는 것 등이다. 고로 컴퓨터에서 Enter는 LF+CR = CR+LF 조합이다.

16~20은 뭔가 다른 기계 장치가 연결 되어 있으면 그걸 제어하려는 명령이다. Escape는 탈출을 의미한다. 보통 무조건 빠져나가는 것을 의미한다. 헌데 이걸 중간에 다른 명령 삽입이라고 해석할 수도 있다. 보통 이런 코드 바로 뒤에 오는 코드는 다른 의미로 해석한다.

21~31, 127은 나중에 추가한 것 같다. 왜냐하면 보통 짝을 지어 나타나는 것들은 함께 제정한 것인데 나중에 추가한 것들은 땜질을 한 것이라 위치가 추가한 순서가 된다. 뭔가 통신 장애에 대한 대책과 메모리 기능이 있는 장치(프린터)에서만 통하는 명령이다. 이미 종이에 찍은 것을 취소하거나 지울 수는 없기 때문이다. 프린터는 자체 메모리가 있어 내용을 적당히 받은 후에 출력을 한다.


2진법
십육진법
십진법
모양
010 0000
20
32
공백
010 0001
21
33
!
010 0010
22
34
"
010 0011
23
35
#
010 0100
24
36
$
010 0101
25
37
%
010 0110
26
38
&
010 0111
27
39
'
010 1000
28
40
(
010 1001
29
41
)
010 1010
2A
42
*
010 1011
2B
43
+
010 1100
2C
44
,
010 1101
2D
45
-
010 1110
2E
46
.
010 1111
2F
47
/
011 0000
30
48
0
011 0001
31
49
1
011 0010
32
50
2
011 0011
33
51
3
011 0100
34
52
4
011 0101
35
53
5
011 0110
36
54
6
011 0111
37
55
7
011 1000
38
56
8
011 1001
39
57
9
011 1010
3A
58
:
011 1011
3B
59
;
011 1100
3C
60
<
011 1101
3D
61
=
011 1110
3E
62
>
011 1111
3F
63
?
100 0000
40
64
@
100 0001
41
65
A
100 0010
42
66
B
100 0011
43
67
C
100 0100
44
68
D
100 0101
45
69
E
100 0110
46
70
F
100 0111
47
71
G
100 1000
48
72
H
100 1001
49
73
I
100 1010
4A
74
J
100 1011
4B
75
K
100 1100
4C
76
L
100 1101
4D
77
M
100 1110
4E
78
N
100 1111
4F
79
O
101 0000
50
80
P
101 0001
51
81
Q
101 0010
52
82
R
101 0011
53
83
S
101 0100
54
84
T
101 0101
55
85
U
101 0110
56
86
V
101 0111
57
87
W
101 1000
58
88
X
101 1001
59
89
Y
101 1010
5A
90
Z
101 1011
5B
91
[
101 1100
5C
92
\
101 1101
5D
93
]
101 1110
5E
94
^
101 1111
5F
95
_
110 0000
60
96
`
110 0001
61
97
a
110 0010
62
98
b
110 0011
63
99
c
110 0100
64
100
d
110 0101
65
101
e
110 0110
66
102
f
110 0111
67
103
g
110 1000
68
104
h
110 1001
69
105
i
110 1010
6A
106
j
110 1011
6B
107
k
110 1100
6C
108
l
110 1101
6D
109
m
110 1110
6E
110
n
110 1111
6F
111
o
111 0000
70
112
p
111 0001
71
113
q
111 0010
72
114
r
111 0011
73
115
s
111 0100
74
116
t
111 0101
75
117
u
111 0110
76
118
v
111 0111
77
119
w
111 1000
78
120
x
111 1001
79
121
y
111 1010
7A
122
z
111 1011
7B
123
{
111 1100
7C
124
|
111 1101
7D
125
}
111 1110
7E
126
~


위의 코드는 종이에(모니터 화면에, 프린터에) 출력할 수 있는 문자이다. 로마 알파벳이 지금의 영어 알파벳의 원조인데 미국에서 만든 표준이라 다른 유럽 언어는 표현할 수 없다. 컴퓨터는 미국에서 먼저 만들었으니 할 수 없다. 그래서 유럽 (8비트로 확장) → 일본(16비트로 확장) → 한국(일본 모방) 등 자기 문자를 표현하려는 노력이 있었다.

아스키와 호환성을 유지하려고 제어 문자를 피하다 보니 코드가 꼬이고 지저분해졌다. 그래서 새로 깔끔하게 세계 문자코드를 만들자는 것이 유니코드인데 인터넷 통신에선 아스키와의 호환성 유지를 하려고 다시 지저분한 버전(UTF-8)을 만들어 사용하고 있다. 컴퓨터 내부에서 처리만 하려면 깔끔한 버전(UTF-16)을 사용하는 게 좋다.




한국은 법은 잘 만들었는데 사람이 지키지 않는다고 한다. 내 생각엔 개소리다.
기계는 잘 못 만들면 제대로 동작하지 않는다. 법도 마찬가지다.
대체로 법을 만드는 사람들은 두루뭉술하고 애매모호하게 만든다. 개구멍이 있다.
그래서 법을 코에 걸면 코걸이 귀에 걸면 귀걸이라고 한다.
정신 나간 대통령 탄핵도 못 하는 나라다. 판사 필요 없으니 배심원제 하자.