반응형
블로그 이미지
개발자로서 현장에서 일하면서 새로 접하는 기술들이나 알게된 정보 등을 정리하기 위한 블로그입니다. 운 좋게 미국에서 큰 회사들의 프로젝트에서 컬설턴트로 일하고 있어서 새로운 기술들을 접할 기회가 많이 있습니다. 미국의 IT 프로젝트에서 사용되는 툴들에 대해 많은 분들과 정보를 공유하고 싶습니다.
솔웅

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리

코로나에서 time, date 다루기

2013. 1. 18. 06:49 | Posted by 솔웅


반응형
Posted on . Written by

“Time keeps on slippin’, slippin’, slippin’, into the future.” —Steve Miller Band / Fly Like An Eagle


어떤 프로그래밍 언어로 time 관련해서 작업을 하는 것은 프로그래머에게 혼란을 주게 됩니다. 이번주의 튜토리얼은 time 과 date 에 대해 다룹니다. 그리고 date 계산, time zone 문제 그리고 date formatting 관련해서 예제를 보여드리겠습니다.


코드를 보기전에 먼저 이해해 둬야할 몇가지 중요한 개념들이 있습니다. 컴퓨터에게는 time 의 기본 유닛은 1초 입니다. 이 값으로 우리는 date 도 계산할 수 있고 fraction 들도 다루고 심지어 time 의 더 작은 순간들도 계산해 냅니다. 사람에게 1초는 짧은 시간이지만 컴퓨터에게는 아주 긴 시간입니다. 사람들도 긴 시간의 경우 세분화해서 사용하듯이 컴퓨터도 마찬가지 입니다. 어떤 함수는 1/1000 초(milliseconds ) 단위로 작업을 하고 이것을 uSec 나 uSeconds으로 쓰기도 합니다. 더 좋은 컴퓨터는 이것보다 더 짧은 시간을 다루기도 합니다. 코로나에서는 3가지 종류의 time measurement 를 사용합니다.




Microseconds and Milliseconds


Microseconds 는 아래와 같이 call 하시면 됩니다 :


system.getTimer()


이 call은 여러분의 app 이 시작한 이후의 시간을 return 해 줍니다. 그 값은 milliseconds (1/1000ths) 단위 입니다. 대부분의 device들에서는 아래와 같은 값을 return 할 겁니다.


1839.3949


이 time은 microsecond resolution 으로 측정되는데요 여러분에게 보일 때는 milliseconds 로 변환해서 보여드리는 겁니다. 그리고 소숫점 아래는 microseconds 를 나타내죠. 이 마이크로세컨드 단위가 필요한 경우가 있을지 궁금해 하실수도 있습니다. 아마 실제로는 필요가 없겠죠. 아마 GPS 데이터를 다루거나 Einstein’s Pedometer (앱 이름인데요. 여러분이 빠르게 움직이면 얼마나 젊어지는지를 보여주는 앱입니다.) 작업을 하게 되면 이 microsecond resolution 이 필요할 수도 있습니다. 하지만 우리의 앱에서는 1초당 30~60 프레임을 사용할 거니까 1/30 에서 1/60 초 단위로 작업을 하게 될 겁니다. 그러니까 1/1000 초 보다 더 정교한 시간계산은 이 앱에서는 그다지 중요하지는 않습니다.


milliseconds와 관련해서 사용하는 함수들은 아래와 같습니다.


이 함수들은 millisecond 단위를 accept 합니다. 그러니까 이 함수에 값을 5000을 주면 그 시간은 5초가 되는 겁니다. 1/2 초는 500이 되겠죠. 2와 1/2 초는 2500이 됩니다.


timer.performWithDelay(500, someFunction)  -- a half second delay


초를 원하는 값으로 바꾸시려면 1000을 곱하면 되겠죠.


Dealing With Longer Periods of Time


위에서 언급했듯이 컴퓨터에서 time 의 기본적인 유닛은 1초입니다. iOS와 안드로이드 (Mac OS X 도 마찬가지로) 모두 Unix 에서 파생된 운영체제에 근거해 있습니다. 그리고 그런 운영체제에서 표준 "time" 함수는 1970년 1월 1일 자정 이후부터 경과된 시간을 초단위로 return 해 줍니다.  마이크로소프트는 좀 다른데요, 하지만 이미 많은 앱들이 C언어를 사용해서 만들어졌고 이 언어는 Unix 에 기반해 있거든요. 그리고 그 언어의 라이브러리도 이 time reference pointEpoch를 사용하구요.


마이너스일 경우에는 1970년 이전이 되겠죠. positive times 일 경우에는 1970년 이후가 되겠구요. 코로나에서는 os.time() API call을 사용해서 1970년 1월 1일 이후 지금까지의 number of seconds 를 얻을 수 있습니다.


print( os.time() )  -- outputs something like: 1358015442


사실 저 숫자만 나온다면 알아볼수가 없으니까 별로 의미가 없겠죠. 이것을 date math로 계산해서 실제 생활에서 쓰이는 날짜와 시간으로 바꿔야 알아볼 수가 있을 겁니다. 그런데 게임을 한번 생각해 보세요. turn-based game 이 있는데 유저가 너무 오랫동안 게임을 하고 있어서 어떤 "nudge" 가 필요한 경우 아래와 같이 마지막 move의 시간을 저장할 필요가 있을 겁니다.


player[1].lastMove = os.time()

그리고 나서 지난 한시간 동안 어떤 move 가 있는지 체크하고 싶으면 아래와 같이 하시면 됩니다.


local now = os.time()
if ( now > player[1].lastMove + 3600 ) then
  -- nudge the player
end


3600은 어디서 왔을까요? 60초 곱하기 60분을 하면 그 값입니다. 즉 1시간이죠. 그러니까 아래와 같이 하셔도 됩니다.


local now = os.time()
if ( now > player[1].lastMove + (60 * 60) ) then
  -- nudge the player
end

Lua 의 pre-processor 는 (60*60) 를 3600으로 convert 할겁니다. 그러면 여러분이 따로 미리 계산할 필요는 없겠죠. 아래와 같이 미리 날짜, 시간, 분을 상수로 정의해서 사용하셔도 됩니다.


DAY = 24 * 60 * 60
HOUR = 60 * 60
MINUTE = 60

local now = os.time()
if ( now > player[1].lastMove + HOUR ) then
  -- nudge the player
end


이렇게 초단위로 다룬 값으로 date math 를 사용하는 것은 아주 쉽습니다.


  • Is your time older than a week? Use ( 7 * 24 * 60 * 60 ).
  • Set an alarm in 30 days? Use ( 30 * 24 * 60 * 60 ).


또한 한달에 몇일이 있는지, 윤년인지 아닌지 등등도 계산할 것을 걱정하지 않으셔도 욉니다.

Working With Dates


Dates(날짜)와 관련해서는 조금 신경을 쓰셔야 됩니다. 왜냐하면 날짜를 표시할 때는 strings 를 사용하니까요.


  • April 1, 2010 4:53pm
  • April 1, 2010 4:53 P.M.
  • 4/1/10 16:53 MT
  • Sun Jan 13 15:17:32 EST 2013
  • 13-JAN-13 15:17


개발자로서 날짜를 component values로 parse 할 필요가 있습니다. date/time string의 각 파트별로 get 하셔야 합니다. (month, day, year, hour, minute, seconds and time zone). 만약에 month를 string 으로 가지고 있다면 이 string 을 number 로 그리고 timestamp로 convert 할 lookup table 을 사용하실 수 있습니다.


local monthNumbers = {}
monthNumbers["January"] = 1
monthNumbers["February"] = 2
monthNumbers["March"] = 3
--etc...
monthAsInteger = monthNumbers[monthString]


가장 일반적인 date format 중의 하나는 ISO-8601 time format입니다. 그 string 값은 대충 아래와 같습니다.


2012-01-12T12:04:35.03-0400


이 date 를 parse 하시려면 이 여러 bit들을  패턴을 사용한 각각의 값으로 break down 하기 위해 string:match string function 을 사용할 겁니다.


local pattern = "(%d+)%-(%d+)%-(%d+)%a(%d+)%:(%d+)%:([%d%.]+)([Z%p])(%d%d)%:?(%d%d)"
local year, month, day, hour, minute, seconds, tzoffset, offsethour, offsetmin =
  dateString:match( pattern )

Lua 를 처음 접하시는 분들 에게는 위 코드가 많이 복잡하게 보일 겁니다. 신택스가 그렇게 간단하지는 않죠? 이해하기 쉽도록 break down 해 보죠.


2012-01-12   T   12:04:35.03   -0400


T 이전 부분은 date 입니다. 알아보기 쉽고 또 나누기 쉽죠.  연,월,일이 숫자로 있고 각각 hyphen 으로 나눠져 있습니다. T는 시간 부분이 시작한다는 의미입니다. 그 다음에는 시,분,초를 가리키는 숫자들이 나오고 각각 colon들로 나눠져 있죠. 이 중 초단위는 floating point number 라는 것을 기억해 두세요. 그 다음부분에는 옵션으로 time zone이 나옵니다. 이 timezone 과 관련해서는 조금 더 얘기를 나눌 겁니다.


string:match() function은 어떤 패턴을 보여주는데요. string 의 span 에 대해 “wildcard” type searching 을 사용하는 그런 패턴이죠. 위의 경우에는 %d+ pattern을 상요해서 숫자를 찾고 있습니다. 이렇게 함으로서 Lua 에게 0에서 9까지의 숫자열을 찾도록 만들죠. + 표시는 한개 이상의 숫자열을 찾는 다는 것을 알려 주는 겁니다. 예를 들어 2012 같은 숫자열을요.


이 값들을 알아볼 수 있는 변수에 저장하기 위해 괄호 () 안에 어떤 pattern set들을 넣게 됩니다. 그러면 Lua에게 우리가 retrieve 하기 원하는 date/time 의 각각의 단위들을 알려줄 수가 있습니다. date 부분은 hyphen 들이 있는데요. 이것은 각 컴포넌트를 분리하는 기호입니다.


(%d+)%-(%d+)%-(%d+) -- gets the year, month and day
%a -- handles the single letter T
(%d+)%:(%d+)%:([%d%.]+) -- gets the hour, minutes and seconds



마지막 부분을 보세요. 숫자하고 점(.) 이 있죠? 초가 floating point value 이기 때문에 이렇게 찾는 겁니다. string matching에서 점(.) 은 “magic character” 입니다. 이 점 앞에 % 를 넣으셔야 Lua 는 실제 점(.) 을 찾을 겁니다.


마지막으로 + 나 - 구분자 다음에 timezone 정보가 옵니다. 혹은 Z 나 UTC 를 사용하기도 하죠. string:match function 은 각각의 match 되는 패턴들에 대한 값들을 return 할 겁니다. 그리고 그 값들을 변수에 담게 되죠. ISO-8601 standard는 timezone time 을 +/-HHMM+/-HH:MM를 사용합니다. 만약 이 timezone 정보가 없으면 여러분이 속해 있는 local timezone 을 사용할 겁니다. 아래의 패턴은 좀 복잡해 보이긴 하는데요. timezone 정보를 처리하는 코드 입니다.


([Z%p])(%d%d)%:?(%d%d)


Z 나 punctuation character 인 + 나 - 다음에 %p가 옵니다. 그 다음에는 두자리 숫자가 와야 됩니다. 그 다음에 colon 은 옵션인데요. 두개의 숫자가 더 올 수도 있습니다. 이 패턴은 tzoffset, offsethour, offsetmin등을 return 합니다. 이것을 date math 에서 사용할 숫자로 convert 하기 위해 os.time() function (documentation) 을 사용합니다.


local timestamp = os.time(
  {year=year, month=month, day=day, hour=hour, min=minute, sec=seconds} )



이제 1970년 1월 1일 이후 몇초가 경과했는지에 대한 값을 갖게 됐습니다. 그리고 이것을 date math 로 이용할 수 있게 됐구요. 한가지 문제가 있는데요. 아직까지 timezone 부분에 대해 일을 마치지는 않았습니다. 다행히 그 작업은 어렵지 않은데요. 지금 이미 기본 시간을 seconds 로 갖고 있으니까 계산만 하면 되곘죠. Z는 time 이 Coordinated Universal Time (UTC)라는 것을 가리키는 겁니다. 만약 이것이 UTC 나 local time 이라면 따로 timezone 과 관련해서 작업할 것은 없습니다.


local offset = 0
if ( tzoffset ) then
  if ( tzoffset == "+" or tzoffset == "-" ) then  -- we have a timezone!
    offset = offsethour * 60 + offsetmin
    if ( tzoffset == "-" ) then
      offset = offset * -1
    end
    timestamp = timestamp + offset
  end
end


os.time()은 여러분이 있는 곳의 timezone 을 사용해서 값을 return 할 겁니다.


아래 코드는 여러분이 라이브러리로 사용할 수 있는 샘플 예제 입니다.


function makeTimeStamp(dateString)
  local pattern = "(%d+)%-(%d+)%-(%d+)%a(%d+)%:(%d+)%:([%d%.]+)([Z%p])(%d%d)%:?(%d%d)"
  local year, month, day, hour, minute, seconds, tzoffset, offsethour, offsetmin =
    dateString:match(pattern)
  local timestamp = os.time(
    {year=year, month=month, day=day, hour=hour, min=minute, sec=seconds} )
  local offset = 0
  if ( tzoffset ) then
    if ( tzoffset == "+" or tzoffset == "-" ) then  -- we have a timezone!
      offset = offsethour * 60 + offsetmin
      if ( tzoffset == "-" ) then
        offset = offset * -1
      end
      timestamp = timestamp + offset
    end
  end
  return timestamp
end


Final note:  여기서는 아직 Daylight Savings Time(써머타임)을 적용하지는 않은 상황입니다. Daylight Savings Time 은 이 튜토리얼에서 다루지 않습니다.


Converting a Timestamp to Something Readable


어떤 경우에는 이 time 을 유저가 알아볼 수 있는 date string으로 표현할 필요가 있을 겁니다. 이게 바로 os.date() function의 목적이죠. (documentation).


os.date() function 은 두개의 파라미터를 받습니다. 둘 다 옵션인데요. 이 옵션 없이 call 하면 현재 있는 지역의 timezone 에 맞는 date/time 을 return 할 겁니다. 아마 아래와 같은 형식일 겁니다.


Sat Jan 12 14:07:30 2013


아주 다양한 형식으로 표현할수 있는데요. 다양한 formatting parameter 들을 이용하시면 됩니다. 이 format parameter들은 Unix/C++ library function strftime에 근거합니다.


print( os.date("%A") )  -- prints out a string representing the weekday.
print( os.date("%A %l:%M%p") )  -- prints out "Saturday 2:30PM"

디폴트로 os.date()가 return 하는 값은 여러분의 지역이나 time zone 에 근거할 겁니다.  RSS feeds, Corona Cloud date 등에서 일반적으로 사용하는 위에서 사용한 날짜 형식을 생성하기 원하신다면 UTC 에 근거한 output 을 받아야 합니다. 이것은 format parameter 앞에 ! 를 붙이시면 됩니다.


print( os.date("%FT%X%z") )  -- outputs: 2013-01-12T15:30:09-0500

or if you wanted it in UTC:

print( os.date("!%FT%XZ") )  -- outputs: 2013-01-12T19:30:09Z



strftime에 있는 다양한 formatting parameter 들은 다양한 format 으로 month, day, year, weekday, am/pm를 얻을 수 있도록 합니다. 



Counting Down Time


흔히 하는 질문으로 "어떻게 countdown timer 를 만들수 있죠?" 가 있습니다.

가장 좋은 방법은 직접 한번 구현해 보는 걸겁니다. 만약에 60안에 한 레벨을 끝내기를 원한다면 60초 후에 뭔가 이벤트를 발생시키기 위해 timer.performWithDelay()를 사용하시면 됩니다. 보기 좋은 UI output 과 함께 좀 더 그럴듯한 방법을 원하신다면 enterFrame event 에 Runtime listener를 사용하실 수도 있습니다. 그렇게 하면 시간을 체크할 수 있습니다. 혹은 1초 timer 를 계속 반복할 수도 있겠죠. 그럼 필요할 때 쉽게 pause 시키고 또 resume 시킬 수 있을 겁니다.


-------- RUNTIME METHOD --------
local startTime = os.time()
local levelTime = 60
local displayTime = display.newText(levelTime, 0, 0, "Helvetica", 20)

local function checkTime(event)
  local now = os.time()
  displayTime.text = levelTime - (now - startTime)
  if ( now > startTime + levelTime ) then
    -- code to end the level
  end
end
Runtime:addEventListener("enterFrame", checkTime)
-------- REPEATING TIMER METHOD --------
local levelTime = 60
local displayTime = display.newText( levelTime, 0, 0, "Helvetica", 20 )

local function checkTime(event)
  levelTime = levelTime - 1
  displayTime.text = levelTime
  if ( levelTime <= 0 ) then
    -- code to end the level
  end
end
timer.performWithDelay( 1000, checkTime, levelTime )



In Summary


Corona에서 time 과 date를 다루는 것은 약간 복잡한 신택스를 다뤄야 합니다. 이 튜토리얼이 그것을 이해하는데 도움이 되셨으면 합니다. 위의 예제들을 활용해서 앱 개발시 이용하시면 훨씬 수월하실 겁니다.


반응형


반응형

어제 이곳 로드 아일랜드 뉴스에는 동성결혼에 대한 얘기가 한창 이슈였습니다.

올해 이곳에서 동성결혼을 허용하는 법안을 만들 것인가 여부가 주의회에서 논의가 시작됐거든요.


이곳에 오기 전에 있었던 뉴저지도 동성결혼 허옹문제가 의회에서 논의되고 공화당 소속 주지사인 크리스티는 이 법안이 통과 되면 거부권을 행사하겠다고 발표하기도 하면서 좀 시끄러웠는데.. 여기도 그러네요.


이곳 New England 지역의 6개 주인 메인, 뉴햄프셔, 버몬트, 메사추세츠, 로드 아일랜드, 코네티넛 중 메인, 코네티컷, 메사추세츠, 뉴 햄프셔, 버몬트는 이미 이 동성 결혼이 법적으로 허락되 있고 로드 아일랜드만 관련 법안이 없는 상황입니다.


하지만 로드 아일랜드도 다른 법에 의해서 실제로 동성 결혼은 이뤄지고 있다고 하는데요. 이번에 제대로 동성 결혼 허용하는 법안에 대해 논의 한다고 합니다.


뉴스에서는 same sex marriage 라고 하는데 이 Providence Journal 에서는 gay marriage 라고 하네요.


기사의 뉘앙스로 봐서는 약간 동성결혼에 대해 긍정적이지는 않은 신문사인것 같습니다.


Nearly 200 opponents of gay marriage rally at R.I. State House


January 15, 2013 5:06 pm
By Philip Marcelo



Providence Journal photo / Philip Marcelo


R.I. Sen. Harold Metts of Providence speaks at the rally in the rotunda Tuesday afternoon.



PROVIDENCE, R.I. -- Opponents of gay marriage are rallying in the State House rotunda Tuesday afternoon, as state lawmakers convene the first hearing on this year's bill to allow gay couples to marry in Rhode Island.


게이 결혼에 반대하는 시위가 주정부 건물인(도청) rotunda 에서 화요일 오후에 있었다. 이날 주 의원들은 Rhode Island 에서 올해 게이 결혼 (동성결혼)을 허락할지에 대한 법안 심사를 하는 첫 날이었다.


With nearly 200 in attendance, church leaders announced a new coalition that is meant to serve as a counterpoint to "Rhode Islanders United for Marriage," which was announced Monday by gay marriage supporters.


200여명이 참가하였고 교회 지도자들은 월요일 게이 결혼(동성결혼) 지지자들이 발표한 "Rhode Islanders United for Marriage 에 대항한 새로운 연대를 발표했다.


The group -- "Faith Alliance to Preserve the Sanctity of Marriage as Established by God" -- includes representatives from the Catholic Church, the Knights of Columbus, the National Organization for Marriage and a number of Hispanic and Latino church groups.

Organizers said the alliance's purpose is to "safeguard God's covenant and definition of marriage." It does not seek to judge or condemn gay, lesbian, or transgender individuals.

More on R.I's gay marriage debate


"신에 의해 만들어진대로 결혼의 신성함을 보존하기 위한 믿음 연합" 이라는 이 단체는 카톨릭, Knights of Columbus, National Organization for Marriage, 그리고 일부 중남미계 교회 그룹으로 구성돼 있다. 이 단체 설립자들은 이 연합의 목표는 신과의 약속과 결혼에 대한 정의를 지키는 것이라고 말했다. 게이, 레즈비언 혹은 트랜스잰더들을 개별적으로 심판하기 위한 단체는 아니다.

More on R.I's gay marriage debate






반응형


반응형

이번에는 getTextUsingTransformations() 함수를 살펴볼 차례죠?


제 생각에 오늘로 소스 분석이 모두 끝날 것 같습니다.

사실 제가 할 작업은 지금까지 분석한 것으로도 충분하거든요.

벌써 미팅에서 결과를 공유했고 그 다음 단계가 진행중입니다.


하지만 시작했으니 마저 끝마쳐보죠.


pdf2text() 함수의 맨 마지막 return 문에 있는 코드 입니다.


return getTextUsingTransformations($texts, $transformations);


파라미터로는 getDirtyTexts() 함수에서 얻었던 $texts 가 첫번째에 있네요.

이 내용은 지난 글 말미에 보여드렸습니다.


두번째 파라미터는 getDecodedStream() 함수를 통해서 얻었던 $data 입니다.


getTextUsingTransformations($texts, $transformations) 함수를 볼까요?


function getTextUsingTransformations($texts, $transformations) {
    $document = "";
    for ($i = 0; $i < count($texts); $i++) {
        $isHex = false;
        $isPlain = false;

        $hex = "";
        $plain = "";
        for ($j = 0; $j < strlen($texts[$i]); $j++) {
            $c = $texts[$i][$j];
            switch($c) {
                case "<":
                    $hex = "";
                    $isHex = true;
                break;
                case ">":
                    $hexs = str_split($hex, 4);
                    for ($k = 0; $k < count($hexs); $k++) {
                        $chex = str_pad($hexs[$k], 4, "0");
                        if (isset($transformations[$chex]))
                            $chex = $transformations[$chex];
                        $document .= html_entity_decode("&#x".$chex.";");
                    }
                    $isHex = false;
                break;
                case "(":
                    $plain = "";
                    $isPlain = true;
                break;
                case ")":
                    $document .= $plain;
                    $isPlain = false;
                break;
                case "\\":
                    $c2 = $texts[$i][$j + 1];
                    if (in_array($c2, array("\\", "(", ")"))) $plain .= $c2;
                    elseif ($c2 == "n") $plain .= '\n';
                    elseif ($c2 == "r") $plain .= '\r';
                    elseif ($c2 == "t") $plain .= '\t';
                    elseif ($c2 == "b") $plain .= '\b';
                    elseif ($c2 == "f") $plain .= '\f';
                    elseif ($c2 >= '0' && $c2 <= '9') {
                        $oct = preg_replace("#[^0-9]#", "", substr($texts[$i], $j + 1, 3));
                        $j += strlen($oct) - 1;
                        $plain .= html_entity_decode("&#".octdec($oct).";");
                    }
                    $j++;
                break;

                default:
                    if ($isHex)
                        $hex .= $c;
                    if ($isPlain)
                        $plain .= $c;
                break;
            }
        }
        $document .= "\n";
    }

    return $document;
}


먼저 $document 변수를 만들어 놓네요. 맨 밑에 보니까 return 될 값이 들어갈 변수입니다.

즉 사람이 읽을 수 있는 완전한 데이터가 들어갈 변수죠.


다음은 $texts 의 count 만큼 for 문을 돌립니다.

그 for문 안에서 맨 먼저 하는 일은 4개의 변수를 정의해 놓는 거네요.

$isHex와 $isPlain 는 boolean 값이 들어갈 변수고 $hex와 $plain 은 어떤 문자 같은게 들어갈 변수인것 같습니다.


여기서 다시 for 문을 돌립니다. $texts 가 이중배열이라서 이렇게 작업하나 봅니다.

두번째 for 문에서는 $c 변수에 $texts[$i][$j] 를 담습니다.


그 다음에 switch 문이 나오는데... 처음에 < 와 > 를 체크하는 군요.


편의를 위해서 어제 봤던 데이터 중 일부를 아래 복사해 넣겠습니다.


(W)-36(e)7(e)7(k )46(o)7(f)
( )
(Oct)-11(o)5(ber )10(1)4( )
<00B2>
( )
(Oct)-11(o)5(ber )10(5)
( )
( )
( )
(Who)5(l)7(e)7( )-19(+ )-2(S)3(um)5( )
( )
( )
(Choice)
( )
(+)
( )
(F)20(lavor)


<00B2> 가 있네요. < 인 경우에는 $isHex 가 true 이고 > 인 경우에는 false 입니다.

그리고 > 인 경우에는 $hex 값을 array 로 바꿉니다.

str_split() 함수에서 그 일을 하죠.


<?php

$str 
"Hello Friend";

$arr1 str_split($str);
$arr2 str_split($str3);

print_r($arr1);
print_r($arr2);

?>


위 소스를 돌리면 아래 값을 얻습니다.

Array
(
    [0] => H
    [1] => e
    [2] => l
    [3] => l
    [4] => o
    [5] =>
    [6] => F
    [7] => r
    [8] => i
    [9] => e
    [10] => n
    [11] => d
)

Array
(
    [0] => Hel
    [1] => lo
    [2] => Fri
    [3] => end
)


어떤 일을 하는 함수인지 아시겠죠?

그 다음에는 $hexs 의 count 만큼  for 문을 돌립니다.

그 다음 그 각각의 값을 지난번에도 나왔던 str_pad() 함수를 사용해서 0을 4칸 붙이네요.


그 다음에 if 문에서 $transformations[$chex] 이 세팅돼 있으면 $chex 변수에 $transformations[$chex]을 대입합니다. 


그리고 나서 $document 에 값을 집어 넣는데요.

html_entity_decode() 함수를 사용합니다.


이 것은 html 을 string 으로 바꿔 주는 함수입니다.


<?php
$orig 
"I'll \"walk\" the <b>dog</b> now";

$a htmlentities($orig);

$b html_entity_decode($a);

echo 
$a// I'll &quot;walk&quot; the &lt;b&gt;dog&lt;/b&gt; now

echo $b// I'll "walk" the <b>dog</b> now
?>


위 예제를 보시면 어떤 일을 하는지 아시겠죠?


그러니까 <> 감싸여진 데이터는 hex 코드라서 이 case 문에서 그것을 처리하는 거네요.


그 다음 case 문에서는 (,) 를 체크합니다.


() 로 둘러싸인 부분은 그냥 text 죠. 사람이 읽을 수 있는..

그러니까 별다른 처리를 하지 않아도 되기 때문에 ) 인 경우에 $document 에 $plain 을 그냥 추가해 버리는 겁니다.


다음에는 \\ 를 체크하는데요.


우리가 다루는 pdf 파일에서는 이 값이 추출 되지 않았습니다.

어쨌든 내용을 보면 줄바꿈, 탭 뭐 이런 것들을 해당 sign 으로 바꿔서 $plain 에 넣는 일을 하네요. 숫자인 경우에는 거기에 맞게 또 처리를 하구요.


디폴트로는 $isHex 일 경우 $hex 에 $c를 추가하고 $isPlain 일 경우 $plain 에 $c를 추가합니다.


그리고 이렇게 만든 $document 를 이전 for 문에서 만든 $document 에 가를 하구요.

이렇게 for 문이 다 돌고 $document 에 값이 다 쌓였으면 이 값을 return 합니다.


그 return 값이 사람이 볼 수 있는 text 입니다.


이렇게 해서 얻은 결과는 아래와 같습니다.



잘 안 보이실 텐데요. 1번 글에서 업로드한 파일들을 다운 받아서 돌려 보시면 됩니다.


참고로 이 데이터를 가지고 요일별 메뉴를 display 하는 함수를 제가 만들어 봤는데요.



function menus($sources){
$resultlen  = strlen($sources);

$menuDate = substr($sources,0,48);
echo "<b><font size=6>Start Menu</font> <p><br> ". $menuDate. "<p></b>";


$result = preg_replace("/\s+/",'_',$sources);

$fs1 = strrpos($result,'_M_');

$menu1 = substr($result,$fs1,$resultlen);

$startTue = strpos($menu1,'_T_');
$startWed = strpos($menu1,'_W_');
$startThu = strrpos($menu1,'_T_');
$startFri = strpos($menu1,'_F_');
$endFri = strpos($menu1,'WEEKLY');

$Monday = str_replace('_', ' ' ,str_replace('_M_','Monday <br>',str_replace('FIT','FIT<br>',substr($menu1,0,$startTue))));
$Tuesday = str_replace('_', ' ' ,str_replace('_T_','Tuesday <br>',str_replace('FIT','FIT<br>',substr($menu1,$startTue,$startWed-$startTue))));
$Wednsday = str_replace('_', ' ' ,str_replace('_W_','Wednsday <br>',str_replace('FIT','FIT<br>',substr($menu1,$startWed,$startThu-$startWed))));
$Thuesday = str_replace('_', ' ' ,str_replace('_T_','Thusday <br>',str_replace('FIT','FIT<br>',substr($menu1,$startThu,$startFri-$startThu))));
$Friday = str_replace('_', ' ' ,str_replace('_F_','Friday <br>',str_replace('FIT','FIT<br>',substr($menu1,$startFri))));

echo "<table width=50%><tr><td>";

echo "<p> ". $Monday . "<p>";
echo "<p> ". $Tuesday . "<p>";
echo "<p> ". $Wednsday . "<p>";
echo "<p> ". $Thuesday . "<p>";
echo "<p> ". $Friday . "<p>";

echo "</td></tr></table><p><br><p> ";
}


그리고 함수 밖에서 이걸 부르면 되죠.

menus($result);

이렇게 하면 아래와 같은 결과가 나옵니다.




이렇게 해서 PDF 를 TEXT 로 변환하는 PHP 프로그램을 모두 분석해 봤습니다.

오랫만에 목욕해서 때를 싹 밀었을 때 처럼 개운하네요.


분석결과 위 소스는 제가 일하는데에서는 맞지 않아서 사용하지 않기로 했거든요.

그래서 저 결과도 깔끔하게 나온 것은 아닙니다.

참고하시구요.


다음에 또 소스 분석할 일 있으면 블로그에 정리해 놓을 께요.

이번 글은 기분 좋게 7번째 만에 마무리 했네요.





반응형