แปลง UTF-8 เป็น TIS-620 ไป ๆ กลับ ๆ ใน .NET Core

ได้ไปทำงานกับลูกค้าที่มี project เก่า ๆ บางตัวที่ยังใช้ข้อมูลในฐานข้อมูลที่มี character set เป็น tis-620 อยู่ และไม่สามารถแปลงเป็น utf-8 ได้ด้วยสาเหตุอะไรบางอย่าง (น่าจะเป็นงานที่จะงอกขึ้นมากับทุก ๆ ระบบที่เค้า live อยู่) รู้แค่ว่า project ใหม่ ๆ ยังต้องไปดึงข้อมูลจากฐานข้อมูลนี้ แถมอีก project ต้องส่งข้อมูลที่เป็น utf-8 ไปหาระบบที่รองรับแต่ tis-620 อีก นั่นคือ ต้องมีวิธีแปลงข้อมูลจาก tis-620 ไปเป็น utf-8 และแปลงจาก utf-8 กลับไปเป็น tis-620

ข่าวร้ายคือ ใน .NET Core Framework ยกเลิกการ support legacy character set บางภาษาไปแล้ว รวมถึงภาษาไทยด้วย นั่นคือ วิธีการ convert encoding ที่ควรจะทำได้ง่าย ๆ มันไม่ง่ายอีกต่อไป

วิธีการตรงไปตรงมาคือ google หาวิธีแปลงจากในเว็บนี่แหละ ก็ไปเจอวิธีแปลงที่มีคนเขียนไว้ของ php (แบบไม่ได้ใช้ iconv) เป็นวิธีแปลงทีละตัวอักษร และเอาผลลัพธ์ที่ได้ไปต่อกันเป็น output ก็เลยลอง port มาเป็น .NET Core ดู ได้ประมาณนี้

using System;
using System.Collections.Generic;
using System.Text;

public static class ThaiEncodingService {
    public static byte[] ToTIS620(string utf8String) {
        List<byte> buffer = new List<byte>();
        byte utf8Identifier = 224;
        for (var i = 0; i < utf8String.Length; i++) {
            string utf8Char = utf8String.Substring(i, 1);
            byte[] utf8CharBytes = Encoding.UTF8.GetBytes(utf8Char);
            if (utf8CharBytes.Length > 1 && utf8CharBytes[0] == utf8Identifier) {
                var tis620Char = (utf8CharBytes[2] & 0x3F);
                tis620Char |= ((utf8CharBytes[1] & 0x3F) << 6);
                tis620Char |= ((utf8CharBytes[0] & 0x0F) << 12);
                tis620Char -= (0x0E00 + 0xA0);
                byte tis620Byte = (byte)tis620Char;
                tis620Byte += 0xA0;
                tis620Byte += 0xA0;

                buffer.Add(tis620Byte);
            } else {
                buffer.Add(utf8CharBytes[0]);
            }
        }
        return buffer.ToArray();
    }

    public static byte[] ToUTF8(byte[] tis620Bytes) {
        List<byte> buffer = new List<byte>();
        byte safeAscii = 126;
        for (var i = 0; i < tis620Bytes.Length; i++) {
            if (tis620Bytes[i] > safeAscii) {
                if (((0xa1 <= tis620Bytes[i]) && (tis620Bytes[i] <= 0xda))
                    || ((0xdf <= tis620Bytes[i]) && (tis620Bytes[i] <= 0xfb))) {
                        var utf8Char = 0x0e00 + tis620Bytes[i] - 0xa0;

                        byte utf8Byte1 = (byte)(0xe0 | (utf8Char >> 12));
                        buffer.Add(utf8Byte1);
                        byte utf8Byte2 = (byte)(0x80 | ((utf8Char >> 6) & 0x3f));
                        buffer.Add(utf8Byte2);
                        byte utf8Byte3 = (byte)(0x80 | (utf8Char & 0x3f));
                        buffer.Add(utf8Byte3);
                    }
            } else {
                buffer.Add(tis620Bytes[i]);
            }
        }
        return buffer.ToArray();
    }
}

จากโค้ดด้านบน ตอนแปลง จาก utf-8 เป็น tis-620 ก็ตรงไปตรงมา โยน utf-8 เข้ามา รับเป็น tis-620 กลับไป สังเกตว่าตอนรับกลับไปรับไปเป็น byte[] เพราะว่าใน dotnet core มันไม่ support string ที่เป็น tis-620 แล้ว

ส่วนตอนแปลงจาก tis-620 ไปเป็น utf-8 ก็เหมือนกัน ต้องรับมาเป็น byte[] ด้วยเหตุผลเดียวกัน ตอนรับกลับไปก็เป็น byte[] ถ้าอยากได้เป็น string ก็ค่อยไปแปลงเอง ด้วย Encoding.UTF8.GetString() ไม่ได้ยากอะไร

ตัวอย่างอยู่ที่ https://github.com/chonla/dotnet-thai-charset-conversion

ป.ล.

  • ทำเป็น Nuget package แล้ว เผื่อใครจะเอาไปใช้กัน https://www.nuget.org/packages/Chonla.ThaiEncoding/
  • ตัวอย่างใน github มีการเอา Linq มาใช้ ทำให้อ่านง่ายขึ้นแล้ว แต่ตรง Conversion logic ยังเหมือนเดิม ขอบคุณ nOBITa3001 สำหรับ PR แจ่ม ๆ ครับ

Leave a Reply

Your email address will not be published. Required fields are marked *