1. 2007.09.19 php에서 파싱시 한글 깨짐 현상
  2. 2007.08.19 XML 생성 Class

php에서 파싱시 한글 깨짐 현상

어디까지나 팁이니까 팁에 해당되는 부분을 위주로 적을께요..
제가 2주동안 혼자서 열라게 피똥싸다가 생각한 거라서 다른 분들도 저처럼 고생하시지
않으셨으면 해서 올려봅니다.
일단 한글깨짐(부분깨짐) 문제가 발생하는 것은 EUC-KR 로 인코딩된 XML 파일을
파싱하고서 출력 할때 나타나는 괴(?) 현상인것 같습니다. 한글이 잘 출력되다가 중간
중간마다 부분적으로 깨지죠.. 예를들면 "이?br>岳?맞도록" 과 같이 잘 출력되다가도
중간에 깨지는 현상이 이에 해당됩니다. 이때 더 환장하게 만드는 점은 마우스 오른쪽
버튼으로 소스보기 하고 텍스트로 보면 희한하게도 정상적으로 나타난다는...
그런데 꼭 익스플로어에서 보면 깨질 겁니다. 이런 경우가 다른 분들도 있을거라 가정
하고서 팁을 적습니다.(QNA 게시판에 비슷한 문제를 격는 분이 있던거 같던데..)

일단 제가 처한 환경은 zip 파일에서 xml 파일을 실시간으로 압축을 풀어서 보여주기
위한 XML 파싱을 하는 거였구요.. 압축을 푼 xml 파일은 euc-kr 포맷인것 같았습니다.
눈으로 보면 다 한글인데 헤더에 EUC-KR 이라고 명시되어 있으니 밑어야죠.. ㅎㅎㅎ
안그렇숩니깡.. <?xml version="1.0" encoding="euc-kr" ?> 이렇게 파일의 헤더에
써있더군요.. 이 XML 파일은 법률정보가 들어있고 태그는 좀 복잡하게 되있습니다.
한글이 많이 들어가 있는 편이고 LEVEL 이 깊다고 해야 하나요..? xml 파싱시의 level...
어쨌든, 일단 xml 파서를 하나 만들었고 아주 기본적인 파서를 쓰기로 했습니다.
소스를 보면 다음과 같습니다. 저 같은 경우는 본문 출력만 필요했기 때문에 다른 부분은
구현 안하고 빈껍데기 함수에 cdataHandler 라는 함수만 사용했습니다.
먼저, 팁의 설명은 문제가 되는 소스와 문제의 출력을 보여드리고 해결하는 방법을
제시하는 형식으로 적어나가겠습니다. 문제가 되었던 파서의 소스부터 보겠습니다.
(이미 한글깨지는 거때문에 수차례 고생을 겪으신 분이면 소스가 눈에 정겹겠죠..? ㅎㅎ)

------------------------- XMLParser.php -------------------------
<?php
class XMLParser {
    var $parser;
    var $out;
    var $encoding = 'ISO-8859-1';

    function XMLParser() {
        $this->_create();
    }

    function _create() {
        $this->parser = @xml_parser_create($this->encoding);

        if (is_resource($this->parser)) {
            //xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
            xml_parser_set_option($this->parser, XML_OPTION_SKIP_WHITE, 1);
            xml_set_object($this->parser, &$this);
            xml_set_element_handler($this->parser, 'startHandler', 'endHandler');
            xml_set_character_data_handler($this->parser, 'cdataHandler');
            return true;
        }
        return false;
    }

    function free() {
        if (is_resource($this->parser)) {
            xml_parser_free($this->parser);
            unset($this->parser);
        }
        return null;
    }

    function startHandler($parser, $element, $attr) {
        $this->tag = $element;
    }

    function endHandler($parser, $element){
    }

    function cdataHandler($parser, $cdata) {
        if (($cdata = trim($cdata)) == '') return;

        switch($this->tag) {
                case 'COURT':
                case 'PRONOUNCEDATE':
                case 'PRONOUNCE':
                case 'CASENUM':
                case 'DECISIONTYPE':
                case 'UNDERLINE':
                case 'LawRef':
                case 'SECTIONTITLE':
                    print $cdata;
                    break;

                case "CASENAME":
                    print '【'.$cdata.'】';
                    break;

                case 'PARA':
                    print '<span style="size: 2pt;">'.$cdata.'</span>';
                    break;

                default:
                    break;
        }
    }

    function parse($data) {
        xml_parse($this->parser, $data);
        return true;
    }
}
?> 
-----------------------------------------------------------
여기까지가 문제의 파서..

------------------------ xmlout.php ------------------------
<?php
require 'XMLParser.php';

$file = 'test.xml';

$buffer = file_get_contents($file);

$xml = new XMLParser();
$xml->parse($buffer);
?>
-----------------------------------------------------------
요기까지가 사용하는 예제...

xmlout.php 를 웹에서 불러들이면 test.xml 파일이 파싱되어 출력됩니다.
일단 저는 test.xml 파일을 열어서 출력을 해봤지요.. 물론 test.xml 파일은 실제로
파싱해야할 파일중에 하나였구요.. XMLParser.php 에 보시면 저는 본문만 출력할려는
목적이었기 때문에 아예 클래스 본체에 박았습니다.
cdataHandler 함수에서 해당 태그에 해당하는 본문을 무조건 출력합니다.
실행시켜봤더니 한글이 아주 잘 나옵니다. 매우 잘...
그런데 위에서 문제 제기를 했던 한글 깨짐이 나타납니다.

------------------- < 예: 한글깨짐 상태 >-------------------
거래의 관행 등을 종합적으로 고려하여 사회정의와 형평의 이?br>岳?맞도록 논리와
경험의 법칙, 그리고 사회일반의 상식과 거래의 통념에 따라 합리적으로 해석하여야 한다.
-----------------------------------------------------------

잘 출력되다가 "이?br>岳?" 이렇게 되길래 분명히 1바이트가 깨져서 겹쳐졌구나라고
생각을 하고 일단은 인코딩을 바꿔봤습니다. encoding 값에 UTF-8 도 줘보고 US-ASCII
도 줘보고 해봤는데 출력은 ISO-8859-1 일때만 출력이 되고 나머지는 출력 조차도
안되더군요..(EUC-KR 은 왜 없는거얏..) 1주가 넘어가고 2주가 넘어가고 서치에 서치를
거듭하면서 지쳐갈 무렵 var_dump 를 우연히 사용해 봤습니다. 사용법은 아래에서 처럼
cdataHandler 함수에 var_dump 를 사용하였습니다.
-----------------------------------------------------------
... 앞부분 생략
    function cdataHandler($parser, $cdata) {
        if(($cdata = trim($cdata)) == '') return;

        var_dump($parser, $cdata);        // 추가 부분
        print '<br>';                     // 웹에서 한줄 띄워보기 위해서..

/*
        switch($this->tag) {
            case 'COURT':
                print $cdata;
                break;

... 생략

            default:
                break;
        }
*/
    }
---------------------------------------------
이렇게 해서 찍어보니 다음과 같이 나오더군요..(출력의 뒷쪽은 짤랐음..)

resource(4) of type (xml) string(552) "[1] 법률행위의 해석은 당사자가 그
어디까지나 당사자의 내심적 의사의 여하에 관계없이 그 서면의 기재
그 객관적인 의미가 명확하게 드러나지 않는 경우에는 그 문언의
종합적으로 고려하여 사회정의와 형평의 이?

resource(4) of type (xml) string(102) "岳?맞도록 논리와 경험의 법칙,

보는 것처럼 처음에는 (552) 문자열이 저장되어 있다고 string(552) 라고 나오면서
맨 뒤에 "이?" 이렇게 깨져있죠.. 파싱하면서 끝에 문자열이 짤리고 뒤에 string(102)에서
그 짤린 문자열부터 들어가있음.. 그래서 한글을 출력하니 깨져서 출력되는 것이었음..
역시나 추측이 확실시 되고나니 일단은 string 값의 한계값을 늘려주면 안짤리지 않을까
라고 생각해서 구글서칭을 해보니 그런 옵션은 있지도 않고 유니코드 UTF-8 을 사용
해서 파싱을 하면 유니코드 파싱을 할 수 있다고 해서 일단은 머리를 굴려보니 문서는
EUC-KR 인것 같은데 UTF-8 인코딩 옵션만 주고 과연 XML 파싱이 될까라는 의문이
들더군요.. 역시나 해보니깐 안됩디다.. 그래서 EUC-KR 문서인 xml 파일을 UTF-8 로
바꾸면 되겠다 싶어서 찾아보니 C 언어에서 쓰는것처럼 iconv 함수가 존재하더군요..
(원래 C 프로그래밍만 주로하다보니.. php 에 iconv 가..)
그래서 일단 xml 파일을 읽어서 내용을 변수로 저장한 후에는 변수만 iconv 함수에
담궜다 빼는 방식으로 EUC-KR 을 UTF-8 로 인코딩 시키기로 했습니다. 그냥 감으로
때려줬는데 잘 작동해서 만족했죠..

--------------------------- xmlout.php -----------------------
<?php
require 'XMLParser.php';

$file = 'test.xml';

$buffer = file_get_contents($file);
$buffer = iconv('EUC-KR', 'UTF-8', $buffer);    // iconv 함수 추가

$xml = new XMLParser();
$xml->parse($buffer);
?>
---------------------------------------------
위와 같이 iconv 함수를 추가했습니다.

그리고 XMLParser.php 파일에 인코딩을 ISO-8859-1 에서 UTF-8 로 바꿔줍니다.
iconv 로 UTF-8 로 인코딩을 바꿔 넘겨주니 파싱을 할때는 당연히 UTF-8 로 파싱을
해야겠죠...

--------------  XMLParser.php --------------------------
class XMLParser {
    var $parser;
    var $out;
    var $encoding = 'UTF-8';    // UTF-8 로 수정

    function XMLParser() {
        $this->_create();
    }

... 생략

    // 전에 추가했던 var_dump 함수부분을 삭제함..
    // 주석처리 했던 switch 부분을 풀음...

... 생략
-----------------------------------------------------------------

이제 다시 실행을 시켜보니... 오우~ 몬가 좀 되나 싶으면서 익스플로어 화면에
읽고싶어도 차마 읽을 수 없는 글씨들이 좌르륵 나오는게 뭔가 좀 인코딩이 많이
됐나 싶더군요... 익스플로어 인코딩메뉴에서 인코딩 방식을 UTF-8 바꿔주니깐
깨지던 한글이 하나도 안깨지고 잘 나타났습니다. 이제 남은 문제는 자동으로 UTF-8
로 익스플로어에 보여지도록 해야 하는데 이게 또 말썽이 발생합디다..
자동으로 익스플로어에 인코딩 UTF-8 이다라는걸 알려주기 위해 별별짓을 다 해보고
header("Content-Type: 어쩌구리.. charset 어쩌구리 UTF-8 로도 지정해보고
<meta 태그 함수도 써봤지만 전혀 효과가 없더군요..
그래서 꽁수를 생각해낸 것이 일단 XML을 UTF-8 로 인코딩해서 파싱하고 난 다음에는
출력만 다시 EUC-KR 로 디코딩(?)해서 출력하면 익스플로어에 정상적으로 출력이
되겠다 싶어 출력부에 iconv 함수를 하나 더 사용했습니다.
    function cdataHandler($parser, $cdata) {

        if (($cdata = trim($cdata)) == '') return;

        $cdata = ($cdata);
        $cdata = iconv('UTF-8', 'EUC-KR', $cdata);

        switch ($this->tag) {
            case 'COURT':
                print $cdata;
위와 같이 출력이 되는 부분 앞단에다가 UTF-8 로 파싱된 것을 다시 EUC-KR 로 돌리도록
했더니 익스플로어에 한글이 아주 자~알 나타나네요.. 물론 깨짐도 없습니다...
저처럼 XML 파싱에 처음 발 담았다가 고생하시는 분이 또 계실까봐 팁을 쏩니다...
긴글 읽어주셔서 감사합니다... ^^

출처 : http://www.phpschool.com/gnuboard4/bbs/board.php?bo_table=tipntech&wr_id=45322


XML 생성 Class

<?php
class Xml_Writer {
    var $xml;
    var $indent;
    var $stack = array();
    function XmlWriter($indent = '  ') {
        $this->indent = $indent;
        $this->xml = ''."\n";
    }
    function _indent() {
        for ($i = 0, $j = count($this->stack); $i < $j; $i++) {
            $this->xml .= $this->indent;
        }
    }
    function push($element, $attributes = array()) {
        $this->_indent();
        $this->xml .= '<'.$element;
        foreach ($attributes as $key => $value) {
            $this->xml .= ' '.$key.'="'.htmlentities($value).'"';
        }
        $this->xml .= ">\n";
        $this->stack[] = $element;
    }
    function element($element, $content, $attributes = array()) {
        $this->_indent();
        $this->xml .= '<'.$element;
        foreach ($attributes as $key => $value) {
            $this->xml .= ' '.$key.'="'.htmlentities($value).'"';
        }
        $this->xml .= '>'.htmlentities($content).''."\n";
    }
    function emptyelement($element, $attributes = array()) {
        $this->_indent();
        $this->xml .= '<'.$element;
        foreach ($attributes as $key => $value) {
            $this->xml .= ' '.$key.'="'.htmlentities($value).'"';
        }
        $this->xml .= " />\n";
    }
    function pop() {
        $element = array_pop($this->stack);
        $this->_indent();
        $this->xml .= "\n";
    }
    function getXml() {
        return $this->xml;
    }
}

$xml = new Xml_Writer();
$array = array(
    array('monkey', 'banana', 'Jim'),
    array('hamster', 'apples', 'Kola'),
    array('turtle', 'beans', 'Berty'),
);

$xml->push('zoo');
foreach ($array as $animal) {
    $xml->push('animal', array('species' => $animal[0]));
    $xml->element('name', $animal[2]);
    $xml->element('food', $animal[1]);
    $xml->pop();
}
$xml->pop();

print $xml->getXml();
?>

'WebDevelop > PHP' 카테고리의 다른 글

PHP란 무엇인가...  (0) 2007.09.17
간단한 페이징 처리  (0) 2007.08.31
history.back()해도 폼값 남아 있기  (0) 2007.08.18
Return top