2009. 5. 4. 15:49

문자열을 길이만큼 나누기

핸드폰 문자 전송등을 보면 80byte 이하만 전송 가능하기 때문에
긴문자열을 80byte 단위로 쪼개야 할때가 간혹 있다.
문자열을 쪼개는 구분점(딱 80byte가 되는 경계선)에 멀티바이트 문자(한글,특수문자등)이 있다면
앞쪽 문자열은 마지막 문자가 깨지는 현상이 생길 것이고
더 큰 건 뒤쪽 문자열은 완전히 깨져버리는 현상이 생긴다.

예를 들자면 "가1나다3" 문자열의 코드를 보면 이 문자열은 아스키 코드로
176,161 => 가
48 => 1
179,170 => 나
180,217 => 다
50 =>3
이렇게 숫자와 영문등은 1byte, 한글은 2byte로 구성되 있다(euckr의 경우)
이런 경우에 2byte씩 이루어진 각 문자의 중간까지만을 끈어내 버리면 어떻게 될까?
문자열을 4만큼 잘라냈다면 '나' 라는 문자의 중간에서 잘라지게 된다.
이러면 "가1?" 처럼 마지막 문자는 원래코드의 절반만 가지게 되므로 이해할수 없는 문자가 출력되게 된다.
더 큰일은 뒤쪽의 문자열은 '나'의 두번째 byte 인 170 부터 해석을 하는데
아스키 코드 127 보다 큰 값들은 2byte씩 해석 되므로 170 과 뒤에 나오는 '다' 문자의 앞쪽 byte인 180이 같이
해석 되버리게 된다. 그럼 뒤에 문자열은 줄줄이 깨져버리게 되는 증상이 발생된다.

이런 경우 잘리는 경계가 되는 문자가 2byte문자의 중간부분이라면 그전 문자까지만 분리를 해야 된는 것이다.


<?php

//문자 쪼개기 작업
function splitString($str,$size) {
	$len = strlen($str);
	if($size >= $len) return array($str);
	
	$rtn = array();
	$flag = 0;
	$start = 0;
	$end = $size-1;
	for($i=0;$i<$len;$i++) {
		if(ord($str[$i]) > 127) $flag++;
		if($end == $i) {
			if($flag%2 == 1) {
				array_push($rtn,substr($str,$start,$size-1));
				$start += $size - 1;
			}
			else {
				array_push($rtn,substr($str,$start,$size));
				$start += $size;
			}
			$end = $start + $size - 1;
		}
	}
	if($len>=$start) {
		array_push($rtn,substr($str,$start,$len-$start));
	}
	
	return $rtn;
}

$a = "ㅂㅈㄷㅂㅈㄷㄴㅇㅁㄴ유ㅜㅍㅋㅌ츠ㅜ뮨ㅇ8뵤3ㅛ2134234ㅗ234ㅡ3ㅍ45ㅠ34ㅜ5ㅊ34ㅍ5ㅊ3ㅠㅌ53ㅠㅍ453ㅠㅍㅊ5ㅠㅍ32ㅊ4퓨2ㅊ42ㅗ34ㅇ2ㅛㅅ3ㅇ4ㅗ2ㅎ3ㅊ4ㅗ23추ㄱㅎㅈㄷㅊ고2ㅎ34ㅎ23ㅊ4ㅜㅈㅊ규ㅜㅈㅊ고2ㅎ34ㅈㅊ구듀궂ㅊ3ㅗㅎ42ㅗㅎ4ㅊ2ㅊ4ㅜ1ㅠ2ㅊ3ㅠㅜ12ㅊ31ㄹ3ㅗ12234234ㅊ234ㅊ2ㅠㅜ3ㅊ42ㅜ34ㅊ234푸ㅍㄷㄱ234ㅊ2ㅍ4ㅜㅠㅊ243ㅜㅠㅊ2";
print_r(splitString($a,80));
?>

Array
(
    [0] => ㅂㅈㄷㅂㅈㄷㄴㅇㅁㄴ유ㅜㅍㅋㅌ츠ㅜ뮨ㅇ8뵤3ㅛ2134234ㅗ234ㅡ3ㅍ45ㅠ34ㅜ5ㅊ34ㅍ5ㅊ3
    [1] => ㅠㅌ53ㅠㅍ453ㅠㅍㅊ5ㅠㅍ32ㅊ4퓨2ㅊ42ㅗ34ㅇ2ㅛㅅ3ㅇ4ㅗ2ㅎ3ㅊ4ㅗ23추ㄱㅎㅈㄷㅊ고2
    [2] => ㅎ34ㅎ23ㅊ4ㅜㅈㅊ규ㅜㅈㅊ고2ㅎ34ㅈㅊ구듀궂ㅊ3ㅗㅎ42ㅗㅎ4ㅊ2ㅊ4ㅜ1ㅠ2ㅊ3ㅠㅜ12ㅊ3
    [3] => 1ㄹ3ㅗ12234234ㅊ234ㅊ2ㅠㅜ3ㅊ42ㅜ34ㅊ234푸ㅍㄷㄱ234ㅊ2ㅍ4ㅜㅠㅊ243ㅜㅠㅊ2
)
지정한 크기만큼만 배열로 만들되 잘라낼 경계의 문자가 2BYTE의 중간이면 그 이전문자까지만 
잘라내는 것이다.
크기만큼 바로 substr 하지 않고 문자열을 모두 순환하는 이유는
바로 잘라낼 경우 아스키코드 127 보다 크면 multi_byte 코드인 것은 인식 할 수 있지만
그코드가 문자의 앞쪽 byte 인지 뒷쪽 byte 인지는 알 방법이 없다.
그렇기 때문에 모든 문자열을 순환하다가 잘라내야할 경계의 byte에 도착하게 되면
$flag 에 지금까지 나온 multi_bypte 코드의 수를 확인하여
짝수였다면 이미 한글자가 만들어졌으니 그냥 자르면 되고 홀수 일 경우 2byte 문자중 1byte만
출력된 것이니 그 문자 앞까지만 잘라내면 되는 것이다.