1. 程式人生 > >C# 關於utf-8的研究

C# 關於utf-8的研究

前提

如果一不小心把字元轉成utf8的格式,但是卻產生了亂碼。這個時候要麼就是尋找其他的轉碼方式,要麼就不想要了,直接過濾吧。

這裡說的是直接過濾的辦法。

參考連結

https://netvignettes.wordpress.com/2011/07/03/how-to-detect-encoding/

大概的程式碼解釋

其實主要的思路就是對照這個表(不過貌似它也不是嚴格對照的),比如下面的程式碼就是對於bytes的數量

private static bool IsLead4(byte b)
{
    return b >= 0xF0 && b < 0xF8;
}
private static bool IsLead3(byte b) { return b >= 0xE0 && b < 0xF0; } private static bool IsLead2(byte b) { return b >= 0xC0 && b < 0xE0; } private static bool IsExtendedByte(byte b) { return b > 0x80 && b < 0xC0; }

接下來就是主要一下特殊字元的邊界情況

if (length >= 4
) { var one = bytes[offset]; var two = bytes[offset + 1]; var three = bytes[offset + 2]; var four = bytes[offset + 3]; if (one == 0x2B && two == 0x2F && three == 0x76 && (four == 0x38 || four == 0x39 || four == 0x2B || four == 0x2F
)) { return UTF7; } else if (one == 0xFE && two == 0xFF && three == 0x00 && four == 0x00) { return UTF32; } else if (four == 0xFE && three == 0xFF && two == 0x00 && one == 0x00) { throw new NotSupportedException("The byte order mark specifies UTF-32 in big endian order, which is not supported by .NET."); } } else if (length >= 3) { var one = bytes[offset]; var two = bytes[offset + 1]; var three = bytes[offset + 2]; if (one == 0xFF && two == 0xFE) { return Unicode; } else if (one == 0xFE && two == 0xFF) { return BigEndianUnicode; } else if (one == 0xEF && two == 0xBB && three == 0xBF) { return UTF8; } } if (length > 1) { // Look for a leading < sign: if (bytes[offset] == 0x3C) { if (bytes[offset + 1] == 0x00) { return Unicode; } else { return UTF8; } } else if (bytes[offset] == 0x00 && bytes[offset + 1] == 0x3C) { return BigEndianUnicode; } } if (IsUtf8(bytes)) { return UTF8; }

接下來就是測試

static void Main(string[] args)
{
    string ch = "";
    string Ja = "らなくちゃ";

    string Re = "фыввфывфывфв";

    //byte[] Rom = {209,132,209,34,90,121,5,34,208};
    //byte[] Rom = { 100,200,3,4,5,6,7,8,9,0,0 };
    byte[] Rom = { 187, 170, 200,253,194,183,211,201,198,247,95,53,71,51,54,};
    //byte[] Rom = {}
    byte[] byteArrayUTF8 = UTF8.GetBytes(Ja);
    //byte[] byteArrayDefault = Encoding.Default.GetBytes(Re);

    string Name = Encoding.UTF8.GetString(Rom,0,(int)Rom.Length);

    var y = GetTextEncoding(Rom);
 }

全部程式碼

using System;
using System.Text;
using static System.Text.Encoding;

namespace ConsoleApp1
{
    class Program
    {
        /// <summary>
        /// Determines whether the bytes in this buffer at the specified offset represent a UTF-8 multi-byte character.
        /// </summary>
        /// <remarks>
        /// It is not guaranteed that these bytes represent a sensical character - only that the binary pattern matches UTF-8 encoding.
        /// </remarks>
        /// <param name="bytes">This buffer.</param>
        /// <param name="offset">The position in the buffer to check.</param>
        /// <param name="length">The number of bytes to check, of 4 if not specified.</param>
        /// <returns>The rank of the UTF</returns>
        public static MultibyteRank GetUtf8MultibyteRank(byte[] bytes, int offset = 0, int length = 4)
        {
            if (bytes == null)
            {
                throw new ArgumentNullException("bytes");
            }
            if (offset < 0 || offset > bytes.Length)
            {
                throw new ArgumentOutOfRangeException("offset", "Offset is out of range.");
            }
            else if (length < 0 || length > 4)
            {
                throw new ArgumentOutOfRangeException("length", "Only values 1-4 are valid.");
            }
            else if ((offset + length) > bytes.Length)
            {
                throw new ArgumentOutOfRangeException("offset", "The specified range is outside of the specified buffer.");
            }
            // Possible 4 byte sequence
            if (length > 3 && IsLead4(bytes[offset]))
            {
                if (IsExtendedByte(bytes[offset + 1]) && IsExtendedByte(bytes[offset + 2]) && IsExtendedByte(bytes[offset + 3]))
                {
                    return MultibyteRank.Four;
                }
            }
            // Possible 3 byte sequence
            else if (length > 2 && IsLead3(bytes[offset]))
            {
                if (IsExtendedByte(bytes[offset + 1]) && IsExtendedByte(bytes[offset + 2]))
                {
                    return MultibyteRank.Three;
                }
            }
            // Possible 2 byte sequence
            else if (length > 1 && IsLead2(bytes[offset]) && IsExtendedByte(bytes[offset + 1]))
            {
                return MultibyteRank.Two;
            }
            if (bytes[offset] < 0x80)
            {
                return MultibyteRank.One;
            }
            else
            {
                return MultibyteRank.None;
            }
        }
        private static bool IsLead4(byte b)
        {
            return b >= 0xF0 && b < 0xF8;
        }
        private static bool IsLead3(byte b)
        {
            return b >= 0xE0 && b < 0xF0;
        }
        private static bool IsLead2(byte b)
        {
            return b >= 0xC0 && b < 0xE0;
        }
        private static bool IsExtendedByte(byte b)
        {
            return b > 0x80 && b < 0xC0;
        }
        public enum MultibyteRank
        {
            None = 0,
            One = 1,
            Two = 2,
            Three = 3,
            Four = 4
        }


        public static bool IsUtf8(byte[] bytes, int offset = 0, int? length = null)
        {
            if (bytes == null)
            {
                throw new ArgumentNullException("bytes");
            }
            length = length ?? (bytes.Length - offset);
            if (offset < 0 || offset > bytes.Length)
            {
                throw new ArgumentOutOfRangeException("offset", "Offset is out of range.");
            }
            else if (length < 0)
            {
                throw new ArgumentOutOfRangeException("length");
            }
            else if ((offset + length) > bytes.Length)
            {
                throw new ArgumentOutOfRangeException("offset", "The specified range is outside of the specified buffer.");
            }
            var bytesRemaining = length.Value;
            while (bytesRemaining > 0)
            {
                var rank = GetUtf8MultibyteRank(bytes, offset, Math.Min(4, bytesRemaining));
                if (rank == MultibyteRank.None)
                {
                    return false;
                }
                else
                {
                    var charsRead = (int)rank;
                    offset += charsRead;
                    bytesRemaining -= charsRead;
                }
            }
            return true;
        }

        /// <summary>
        /// Uses various discovery techniques to guess the encoding used for a byte buffer presumably containing text characters.
        /// </summary>
        /// <remarks>
        /// Note that this is only a guess and could be incorrect.  Be prepared to catch exceptions while using the <see cref="Encoding.Decoder"/> returned by
        /// the encoding returned by this method.
        /// </remarks>
        /// <param name="bytes">The buffer containing the bytes to examine.</param>
        /// <param name="offset">The offset into the buffer to begin examination, or 0 if not specified.</param>
        /// <param name="length">The number of bytes to examine.</param>
        /// <returns>An encoding, or <see langword="null"> if one cannot be determined.</returns>
        public static Encoding GetTextEncoding(byte[] bytes, int offset = 0, int? length = null)
        {
            if (bytes == null)
            {
                throw new ArgumentNullException("bytes");
            }
            length = length ?? bytes.Length;
            if (offset < 0 || offset > bytes.Length)
            {
                throw new ArgumentOutOfRangeException("offset", "Offset is out of range.");
            }
            if (length < 0 || length > bytes.Length)
            {
                throw new ArgumentOutOfRangeException("length", "Length is out of range.");
            }
            else if ((offset + length) > bytes.Length)
            {
                throw new ArgumentOutOfRangeException("offset", "The specified range is outside of the specified buffer.");
            }
            // Look for a byte order mark:
            if (length >= 4)
            {
                var one = bytes[offset];
                var two = bytes[offset + 1];
                var three = bytes[offset + 2];
                var four = bytes[offset + 3];
                if (one == 0x2B &&
                    two == 0x2F &&
                    three == 0x76 &&
                    (four == 0x38 || four == 0x39 || four == 0x2B || four == 0x2F))
                {
                    return UTF7;
                }
                else if (one == 0xFE && two == 0xFF && three == 0x00 && four == 0x00)
                {
                    return UTF32;
                }
                else if (four == 0xFE && three == 0xFF && two == 0x00 && one == 0x00)
                {
                    throw new NotSupportedException("The byte order mark specifies UTF-32 in big endian order, which is not supported by .NET.");
                }
            }
            else if (length >= 3)
            {
                var one = bytes[offset];
                var two = bytes[offset + 1];
                var three = bytes[offset + 2];
                if (one == 0xFF && two == 0xFE)
                {
                    return Unicode;
                }
                else if (one == 0xFE && two == 0xFF)
                {
                    return BigEndianUnicode;
                }
                else if (one == 0xEF && two == 0xBB && three == 0xBF)
                {
                    return UTF8;
                }
            }
            if (length > 1)
            {
                // Look for a leading < sign:
                if (bytes[offset] == 0x3C)
                {
                    if (bytes[offset + 1] == 0x00)
                    {
                        return Unicode;
                    }
                    else
                    {
                        return UTF8;
                    }

                }
                else if (bytes[offset] == 0x00 && bytes[offset + 1] == 0x3C)
                {
                    return BigEndianUnicode;
                }
            }
            if (IsUtf8(bytes))
            {
                return UTF8;
            }
            else
            {
                // Impossible to tell.
                return null;
            }
        }
        static void Main(string[] args)
        {
            string ch = "";
            string Ja = "らなくちゃ";

            string Re = "фыввфывфывфв";

            //byte[] Rom = {209,132,209,34,90,121,5,34,208};
            //byte[] Rom = { 100,200,3,4,5,6,7,8,9,0,0 };
            byte[] Rom = { 187, 170, 200,253,194,183,211,201,198,247,95,53,71,51,54,};
            //byte[] Rom = {}
            byte[] byteArrayUTF8 = UTF8.GetBytes(Ja);
            //byte[] byteArrayDefault = Encoding.Default.GetBytes(Re);

            string Name = Encoding.UTF8.GetString(Rom,0,(int)Rom.Length);

            var y = GetTextEncoding(Rom);
         }
    }
}