많은 것이 진행되었습니다. 그리고 새로운 구문이 추가되었습니다. 그러면 예시에 나온 코드를 하나씩 분해해서 이해해 봅시다.
| myFile myStream myLine addrIP mySet contents sFound eFound logDirectory |
이번 코드에서 사용되는 임시변수입니다. 각 변수는 스페이스로 구분되며, 전체를 종선으로 둘러쌉니다.
Transcript 내용을 클리어(표시내용을 삭제)하는 단순 메시지입니다.
logDirectory := 'c:\vw7.7\image'.
임시변수에 서버 로그파일의 위치를 대입합니다. 디렉토리명은 사용하시는 시스템, 네트워크상의 폴더, 파일 시스템, 디렉토리 이름이 되도록 해주십시오.
contents := logDirectory asFilename directoryContents.
디렉토리 내용을 수집하여 배열에 파일명을 격납합니다. logDirectory 임시변수에는 디렉토리명이 문자열로써 포함되어 있습니다. 때문에, Filename 객체로 변환하기 위해서는 asFilename 메시지를 logDirectory에 전송합니다. 그리고 Filename 객체가 가지고 있는 directoryContents 메시지에 의해 그 디렉토리에서 모든 파일명을 골라내는 작업을 합니다.
루프 개시입니다. 임시변수 contents(지정된 디렉토리의 모든 파일을 일람한)의 내용을 처음부터 끝까지 반복합니다.
첫 블록식의 개시입니다. 블록 첫 부분은 임시변수가 선언된 선언부입니다. 이 경우, :each가 파일명(contents 배열의 각 요소)을 격납하기 위해서 사용되는 임시변수입니다. 선언부는 세로줄로 종료합니다.
sFound := each findString: 'ws00' startingAt: 1.
Contents 배열의 각 요소는 문자열입니다. String 클래스의 인스턴스는 findString:startingAt: 메시지를 인식합니다. 각 이름이 나타내듯, 특정 위치에서 시작하는 문자열 중에서 문자열을 찾습니다. 파일명(readme.txt"와 같이)을 부여하면, 파일명의 첫 문자에서 개시하여 문자열(연속하는 문자)"ws00"를 검색합니다. 이 메서드는 수치를 돌려줍니다. 여기서는 "ws00"문자열의 개시 위치입니다. 그리고, 그렇게 돌아온 값이 임시변수 sFound에 격납됩니다. 만일 돌아온 값이 0(제로)일 경우, 문자열을 찾을 수 없었던 것을 의미합니다. 따라서, 그 파일은 "ws00"파일이 아닌 것은 확인할 수 있습니다.
eFound := each findString: '.log' startingAt: 7.
앞의 코드와 같이, 특정 문자열을 지정위치에서부터 지정문자를 찾아냅니다. 따라서, 파일명의 7번째 문자에서 시작하여 ".log"문자를 찾아냅니다. 메서드의 결과는 수치입니다. - ".log"로 시작하는(이 경우에는 9입니다.) 문자위치 – 그리고, eFound 임시변수에 값을 격납합니다. 돌아온 값이 제로일 경우, 지정 파일이 ".log"파일이 아니었기에 문자열을 찾을 수 없었다는 것을 의미합니다.
다음 코드 블록의 개시입니다. sFound > 0 메서드에서 돌아온 값이 참이거나 eFound > 0 메서드에서 돌아온 값이 참일 경우, 아래의 코드 블록을 실행합니다. sFound > 0 메서드에서 돌아온 값이 거짓일 경우, eFound > 0 메서드에서 돌아온 값이 거짓일 경우, 아래의 코드 블록을 스킵합니다.
두 번째 블록식의 첫 행입니다. 블록파일을 찾았기에, 중복되지 않는 IP 어드레스를 카운트하는 코드가 시작됩니다. 우선 새로운 Set을 작성하는 것부터 시작합니다.
myFile := (logDirectory, '\', each) asFilename.
Smalltalk에서는 파일 위치를 명확히 전달할 필요가 있습니다. 임시변수 contents의 내용은 단순한 파일명의 일람입니다. 디렉토리명을 지정해서 파일명을 취득하고 있음에도, Smalltalk는 디렉토리를 기억하지 못합니다. 따라서, 디렉토리명이 격납된 변수와 파일명을 결합할 필요가 있습니다. 백슬래쉬("\") 문자는 디렉토리명과 파일명 사이에 사용됩니다.(VisualWorks 일본판에서는 엔(円)마크가 "\"가 됩니다.) 콤마 메서드는 문자열끼리 결합합니다. 이 식 전체에서 괄호 안의 내용을 맨 처음 실행합니다. 그리고 파일명에 문자열을 변환하는 asFilename 메시지를 보냅니다. 이것들의 결과를 임시변수 myFile에 대입합니다.
myStream := myFile readStream.
이 구문은 stream에 파일을 변환시켜줍니다. 그리고 변환된 결과를 임시변수 myStream에 대입합니다.
[ myStream atEnd ] whileFalse:
세 번째 (맨 마지막) 블록식의 시작입니다. 블록 맨 처음 부분은 논리식(불 논리 – Boolean logic)입니다. 스트림 맨 마지막(즉 파일 맨 마지막)에 있는지 조사합니다. 그리고 참(스트림의 맨 마지막입니다)인가 거짓(또는 맨 마지막이 아닙니다)을 돌려줍니다. 이 결과는 whileFalse: 메시지에 보냅니다. stream의 맨 마지막이 올때까지 Smalltalk는 이 메시지에 이어지는 블록식을 처리합니다. Stream 맨 마지막에 도달했을 때, Smalltalk는 이 메시지에 이어지는 모든 블록식을 날려버립니다.
[ myLine := myStream upTo: Character cr.
세 번째 블록식의 맨 처음 문입니다. 문자 cr이 블록파일에 대해 행의 마지막을 나타냅니다. 이 행에서는 cr문자에 다다를 때까지 모든 문자를 취득하여 임시변수 myLine에 취득문자열을 대입합니다.
addrIP := myLine copyUpTo: $,.
임시변수 myLine의 내용은 문자열입니다. 로그파일의 IP 어드레스는 콤마 구분 분자열의 맨 처음 필드이기 때문에, 맨 처음 콤마에 다다를 때까지 모든 문자를 복사할 필요가 있습니다. 콤마는 Smalltalk에서 특별한 의미를 지니고 있기에(힌트 : 바로 위 문을 참조해주십시오), 달러마크($) 앞에 붙임으로 인해 특별한 의미를 무효화시키고, 콤마문자로써 취급하도록 Smalltalk에 전달합니다.
세 번째 블록식의 끝입니다.(whileFalse: 조건이 파일 끝이 된 상태)
이 문에 다다른 때는, 파일 끝에 달했다는 의미입니다. 여기서는 stream을 닫음으로 인해 파일을 닫습습니다. 파일을 stream으로 변환했기에 stream을 닫을 필요가 있습니다.
Transcript show: each, '...', mySet size printString; cr.
1행 끝에는 코드가 많은 느낌이 듭니다. 하지만 실은 그렇지 않습니다. 지금까지의 작업에서 지정된 파일이 지닌 Hit수를 알고 있습니다. 그렇기에, Transcript에 그 수를 단순히 표시하면 됩니다. 그러면 Transcriptshow:를 설명하겠습니다. each에는 파일명이 포함되어 있습니다. 콤마를 사용하여 파일명과 세 개의 도트 문자열('…')을 결합합니다. 그리고 mySet size printString을 사용해서 Hit 수도 결합합니다. show: 메서드는 수치를 받아들이지 않습니다. 문자열만 받아들입니다. 그렇기에, mySet size식은 수치를 돌려주기에 printString을 사용해서 문자열을 변환하고 있습니다. 맨 마지막에는 cr 메서드를 Transcript 객체(세미콜론)에 cascade합니다. 이 결과, 파일의 웹Hit가 표시될 때마다 개행됩니다.
두 번째 블록식의 끝입니다.(ifTrue: 조건이 로그파일을 찾았는지 안찾았는지의 상태)
첫 블록식의 끝입니다.(do:가 배열의 요소를 반복하고 있음)
|