๐ Problem
php โ java๋ก ์ปจ๋ฒํ ์ ํ๊ณ php ์๋ฒ์ api์ java ์๋ฒ์ api์ ์๋ต ๋ฐ์ดํฐ๋ฅผ ์์ ํ ๋์ผํ๊ฒ ๋ง์ถ๋ ๋ฐฉ์์ผ๋ก
ํ ์คํธ๋ฅผ ์งํํ๋ค. ๊ธฐ๋ฅ์ ๋ฐ๊พธ๋ ๊ฒ์ด ์๋ ์ฝ๋ ์ปจ๋ฒํ ๋ง ํ๊ธฐ ๋๋ฌธ์ php์๋ฒ์ ์๋ต๊ณผ 100% ๋์ผํด์ผ ํ๋ค.
๋ฐ์ ๋ฌธ์
๋ฌธ์์ด์ ์ต๋ 150๋ก ์๋ผ์ผ ํ๋ค.
php์์๋ ๊ธฐ๋ณธ api๋ฅผ ์ฌ์ฉํด์ ๋ฌธ์์ด์ ์๋ฅด๋ฉด ๋ฐ์ดํธ ๊ธฐ์ค 150๊ฐ๋ก ์๋ฅธ๋ค. ์ด๋ ํ๊ธ์ 2๋ฐ์ดํธ๋ก ์ธ์ํ๋ค.
ํ์ง๋ง java๋ ๋ฌธ์์ด ์์ฒด๋ฅผ ๊ธฐ์ค์ผ๋ก ๋๊ธฐ ๋๋ฌธ์ 2๋ฐฐ ์ ๊ฒ ๊ธธ์ด๊ฐ ์ธก์ ๋๋ค.
์๋ฅผ ๋ค๋ฉด ์๋์ ๊ฐ๋ค.
์ธ์ด | ์๋ฅผ ๊ธธ์ด | ํ๊ฒ ๋ฌธ์์ด | ๊ฒฐ๊ณผ |
---|---|---|---|
php | 4 | ์๋ ํ์ธ์ | ์๋ |
java | 4 | ์๋ ํ์ธ์ | ์๋ ํ์ธ |
java์์ StringUtils.left(string, limit)
๋ฅผ ์ฌ์ฉํ๋ค.
์ด๋ ๋จ์์ด ๋ฌธ์์ด์ ์ผ์ชฝ๋ถํฐ 4์๋งํผ ์๋ฅด๋ ๊ฒ์ด๋ค.
๐ Trial And Error
Charset๋ณ๋ก ๋์ฝ๋ฉ
๋จผ์ ํ๊ธ์ ๋ช ๋ฐ์ดํธ๋ก JVM์ด ์ฒ๋ฆฌํ๋์ง ์์๋ดค๋ค.
์๋ ์ฝ๋๋ก ํ์ธํ ๊ฒฐ๊ณผ 3๋ฐ์ดํธ๋ผ๋ ๊ฒ์ ํ์ผํ ์ ์์๋ค.
"์๋
ํ์ธ์".getBytes().length // 15
์ด์ 3๋ฐ์ดํธ๋ผ๋ ๊ฒ์ ์์๊ธฐ ๋๋ฌธ์ ์ด๊ฑธ 2๋ฐ์ดํธ๋ก ๋ฐ๊พธ๋ฉด ๋๊ฒ ๋ค๊ณ ์๊ฐํ๋ค.
๊ทธ๋์ 2๋ฐ์ดํธ๋ก ์ฒ๋ฆฌํ๋ Charset์ ์ฐพ๊ธฐ ์ํด ๋ชจ๋ Charset์ ์ฌ์ฉํด์ String์ผ๋ก ๋์ฝ๋ฉ ํด๋ดค๋ค.
ํ์ง๋ง ๋ชจ๋ ๋ฌธ์๋ ๊นจ์ก๋ค. ํ์ง๋ง UTF-8๋ก ํ๋ฉด ๊นจ์ง์ง ์์๋ค.
// org.apache.commons.lang3.StringUtils
StringUtils.left(new String("ํ๊ธ์ด์ฉ๊ตฌ".getByte(), UTF-8) ,150).trim()
Java๋ OS์ ์์คํ Charset์ ๋ํดํธ๋ก ์ฑํํ๊ณ ์๋ค.
JVM์ ๋ํดํธ Charset์ OS Charset์ผ๋ก ํ๋ค๋ ๊ฒ์ ์๊ฒ ๋์๋ค.
์๋ ์ฝ๋๋ก ํ ์คํธ ๊ฒฐ๊ณผ๋ก ํ์ธํ ์ ์์๋ค.
๋ด ์ปดํจํฐ์ Charset์ UTF-8์ด์๊ณ , ๊ทธ๋์ ์์์ Charset๋ณ๋ก ๋์ฝ๋ฉ์ ์๋ํ์ ๋ UTF-8์ ์ ์ธํ๊ณ ๋ ๋ชจ๋ ๊นจ์ก๋ ๊ฒ์ด๋ค.
์ฆ, ์ธ์ฝ๋ฉ์ด ๋๋ฉด Charset์ ๋ฐ๊ฟ ์๊ฐ ์๋ค. ์ด๋ฏธ ์ฝ์๋ ํ๋กํ ์ฝ๋ก ์ธ์ฝ๋ฉ ๋๊ธฐ ๋๋ฌธ์
๋์ฝ๋ฉ์ ๋ค๋ฅธ Charset์ผ๋ก ํ๋ฉด ์๋๋ ๊ฒ์ด๋ค.
@Test
void ์ธ์ฝ๋ฉ() {
// given
String twoByteStr = "์";
String encode16 = UriUtils.encode(twoByteStr, StandardCharsets.UTF_16);
String decode8 = UriUtils.encode(twoByteStr, StandardCharsets.UTF_8);
System.out.println(Charset.defaultCharset()); // UTF-8
System.out.println("decode8 = " + decode8); // decode8 = %EC%95%88
System.out.println("encode16 = " + encode16); // encode16 = โไฅไโใ
}
์ธ์ฝ๋ฉ ๋ฌธ์ ๊ฐ ๋ง์์๊น?
์ธ์ฝ๋ฉ ์ด์๋ ์๋๋ผ๊ณ ๋ณผ ์ ์์ ๊ฒ ๊ฐ๋ค.
php ์๋ฒ์์๋ utf-16์ธ์ง๋ ๋ชจ๋ฅด๊ฒ ์ง๋ง 2๋ฐ์ดํธ๋ก ํ๊ธ์ ์ทจ๊ธํ๋ Charset์ ์ฌ์ฉํ๋ ๊ฒ์ด๋ค.
๋ฌผ๋ก ๊ทธ๋ ๋ค๊ณ ํ๋ค ๋ฐ์ดํธ ๊ธฐ์ค์ผ๋ก ์๋ฅด๋ ๊ฒ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ชป์ฐพ์๊ธฐ ๋๋ฌธ์ ๊ฒฐ๊ตญ ๋๋ ์ง์ 2๋ฐ์ดํธ๋ก ํ๊ธ์ ์๋ฅด๋ ์ฝ๋๋ฅผ
์์ฑํ๋ค. ๋ง์ฝ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฐพ์๋๋ฐ, ๊ฑฐ๊ธฐ์ defaultCharset()์ ์ฌ์ฉํ๋ค๋ฉด ์ธ์ฝ๋ฉ ๋ฌธ์ ๊ฐ ๋์ ์๋ ์๋ค.
์์์ ํ ์คํธํ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด ์ ์ ์๋ฏ์ด defaultCharset() OS์ Charset์ ์ฝ์ด์ค๊ธฐ ๋๋ฌธ์ด๋ค.
๊ทธ๋ฌ๋๊น ์ ์ด์ ์ธ์ฝ๋ฉ ๋ฌธ์ ์๋ค๊ธฐ ๋ณด๋ค๋ php์์ ๋ฌธ์์ด์ ๊ทธ๋๋ก ์๋ฅด๋๋ ๋ช ๋ฐ์ดํธ ๊ธฐ์ค์ผ๋ก ์๋ฅด๋๋์ ๋ฌธ์ ์๋ ๊ฒ ๊ฐ๋ค.
์๋ฐ์์๋ 3๋ฐ์ดํธ๋ก ์ธ์ํ๋ ๊ฒ๋ ๋ฌด๊ดํ ๊ฒ ์๋๊ฐ?
ํ๊ธ์ 2๋ฐ์ดํธ๋ก ์ธ์ํ๋ Charset์ ์์๊น?
UTF-8์ 3๋ฐ์ดํธ๋ก ์ธ์ํ๊ณ UTF-16์ 2 or 4๋ฐ์ดํธ๋ก ์ธ์ํ๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ defaultCharset()์ ์ฌ์ฉํ๋ค๋ฉด os์ Charset์ ๋ฐ๊ฟ์ผ ํ๋ค.
์๋๋ฉด JVM ์ต์ ์ ๋ฐ๊พธ๋ ๋ฐฉ๋ฒ๋ ์์ง ์์๊น?
โ ๏ธ ์ ๋ฌธ์์ด ์์ฒด๊ฐ ์๋ ๋ฐ์ดํธ ๋จ์๋ก ์๋ผ์ ์ฌ์ฉํด์ผ ํ ๊น?
๋น์ง๋์ค์ ๋ฌธ์ ์ด๋ค. ์๋ฅผ ๋ค์ด ๋๋น์ ์ปฌ๋ผ ์ฌ์ด์ฆ ๋๋ฌธ์ด๋ผ๋์ง ์๋๋ฉด ํต์ ํ๋๋ฐ ์์ด์ ๊ท์น์ด ๊ทธ๋ ๋ค๋์ง ๋ฑ๋ฑ..
๐ Solution
๋ฐ์ดํธ ๊ธฐ์ค์ผ๋ก ์๋ฅด๋๋ก ๋ณ๊ฒฝ
์๋ฐ or ์คํ๋ง์์ ์ ๊ณตํ๋ ๋ฐ์ดํธ ๊ธฐ์ค์ผ๋ก ์๋ฅด๋ ๊ฒ์ ์ฐพ์ง ๋ชปํด์ ์ง์ ๊ตฌํํ๋ ๋ฐฉ์์ ํํ๋ค.
๊ฒฐ๊ตญ 2๋ฐ์ดํธ ๊ธฐ์ค์ผ๋ก ์๋ผ์ผ ํ๋ ์ํฉ์ด์๊ธฐ ๋๋ฌธ์ ํํ๊ฒ ๋์๋ค.
code
public class ByteString { /** * String์์ ํ๊ธ์ด ์์ ๊ฒฝ์ฐ ํ๊ธ์ 2byte or 3byte๋ฑ์ผ๋ก ์ธ์ํ๊ฒ ํ์ฌ * ๋ฐ์ดํธ ๊ธฐ์ค์ผ๋ก ๋ฌธ์์ด์ ์๋ผ์ ๋ฌธ์์ด์ ๋ฐํํ๋ค. * @param target ์๋ฅผ ๋ฌธ์์ด * @param limit ๋ฐ์ดํธ ๊ธฐ์ค์ผ๋ก ์๋ฅผ limit * @param standardByteLength ํ๊ธ์ ๋ช ๋ฐ์ดํธ๋ก ์ทจ๊ธํ ์ง ๊ธธ์ด * @return ๋ฐ์ดํธ ๊ธฐ์ค์ผ๋ก ์๋ฆฐ ๋ฌธ์์ด */ public static String cutStringToByte(String target, int limit, int standardByteLength) { TargetString doggy = new TargetString(target); return doggy.leftStringToByteCuting(limit, standardByteLength); } private static class TargetString { private final String target; private int realLimit = 0; private int bufferSize = 0; public TargetString(String target) { this.target = target; } public String leftStringToByteCuting(int standardLimit, int standardByte) { for (int i = 0; i < target.length(); i++) { char ascii = target.charAt(i); boolean isFillBuffer = fillBuffer(ascii, standardLimit, standardByte); if (!isFillBuffer) { return target.substring(0, realLimit); } } return target.substring(0, realLimit); } private boolean fillBuffer(char c, int standardLimit, int standardByte) { if (isAsciiCode(c)) { return fillEnglishBuffer(standardLimit); } return fillKrBuffer(standardLimit, standardByte); } private static boolean isAsciiCode(int asciiCode) { return asciiCode <= 127; } private boolean fillEnglishBuffer(int standardLimit) { if (isFillEnglish(standardLimit)) { bufferSize++; realLimit++; return true; } return false; } private boolean fillKrBuffer(int standardLimit, int standardByte) { if (isFillKr(standardLimit, standardByte)) { bufferSize += standardByte; realLimit++; return true; } return false; } private boolean isFillEnglish(int standardLimit) { return standardLimit > bufferSize; } private boolean isFillKr(int standardLimit, int standardByte) { return standardLimit >= bufferSize + standardByte; } } }
๐์ฐธ๊ณ ์๋ฃ
https://codingpractices.tistory.com/entry/์ธ์ฝ๋ฉ-vs-๋์ฝ๋ฉ-์ ํํ๊ฒ-์ดํดํ๊ธฐ
https://it-eldorado.tistory.com/143
https://codingpractices.tistory.com/entry/์ธ์ฝ๋ฉ-vs-๋์ฝ๋ฉ-์ ํํ๊ฒ-์ดํดํ๊ธฐ