【问题标题】:How to use Encoding.GetEncoding() to get National standards Code Page?如何使用 Encoding.GetEncoding() 获取国家标准代码页?
【发布时间】:2015-03-26 09:27:34
【问题描述】:

我想在将字符串发送到串行端口之前对其进行编码,如下所示:

SerialPort port = new SerialPort(portName, baudRate, Parity.None, 8,   StopBits.One);
                    port.DtrEnable = true;
Encoding encode = Encoding.GetEncoding("VISCII"); 
port.Encoding = encode;

VISCII 是我的语言代码页。当然,在这种情况下我得到了例外,因为它不受支持。

我该怎么做? 任何答案将不胜感激! 非常感谢

【问题讨论】:

  • 我会说.NET本身不支持VISCII,所以你必须编写你的编码器。
  • 与您通话的设备知道任何有关 VISCII 的几率应该非常接近于零。如果你不描述它,那么你就无法得到一个像样的猜测。
  • 没错!我的设备是不包含 VISCII 代码页的 POS 打印机(在这种情况下是越南字符)。

标签: c# encoding character-encoding


【解决方案1】:

这里有一个(不那么)简单的 VISCII 编码/编码器/解码器:

namespace Utilities
{
    using System;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Runtime.CompilerServices;
    using System.Text;

    /// <summary>
    /// VISCII (https://en.wikipedia.org/wiki/Vietnamese_Standard_Code_for_Information_Interchange)
    /// encoding for C#.
    /// Fast table-based implementation not based on MappedEncoding.
    /// Fully thread safe/reentrant (because VISCIIEncoder is fully thread
    /// safe/reentrant and VISCIIDecoder is always used with flush = true).
    /// </summary>
    public class VISCIIEncodingSimple : Encoding
    {
        // Taken from http://en.wikipedia.org/wiki/Vietnamese_Standard_Code_for_Information_Interchange
        public static readonly ReadOnlyCollection<char> Unicodes = Array.AsReadOnly(new char[] 
        {
            '\u0000', '\u0001', '\u1EB2', '\u0003', '\u0004', '\u1EB4', '\u1EAA', '\u0007', 
            '\u0008', '\u0009', '\u000A', '\u000B', '\u000C', '\u000D', '\u000E', '\u000F', 
            '\u0010', '\u0011', '\u0012', '\u0013', '\u1EF6', '\u0015', '\u0016', '\u0017', 
            '\u0018', '\u1EF8', '\u001A', '\u001B', '\u001C', '\u001D', '\u1EF4', '\u001F', 
            '\u0020', '\u0021', '\u0022', '\u0023', '\u0024', '\u0025', '\u0026', '\u0027', 
            '\u0028', '\u0029', '\u002A', '\u002B', '\u002C', '\u002D', '\u002E', '\u002F', 
            '\u0030', '\u0031', '\u0032', '\u0033', '\u0034', '\u0035', '\u0036', '\u0037', 
            '\u0038', '\u0039', '\u003A', '\u003B', '\u003C', '\u003D', '\u003E', '\u003F', 
            '\u0040', '\u0041', '\u0042', '\u0043', '\u0044', '\u0045', '\u0046', '\u0047', 
            '\u0048', '\u0049', '\u004A', '\u004B', '\u004C', '\u004D', '\u004E', '\u004F', 
            '\u0050', '\u0051', '\u0052', '\u0053', '\u0054', '\u0055', '\u0056', '\u0057', 
            '\u0058', '\u0059', '\u005A', '\u005B', '\u005C', '\u005D', '\u005E', '\u005F', 
            '\u0060', '\u0061', '\u0062', '\u0063', '\u0064', '\u0065', '\u0066', '\u0067', 
            '\u0068', '\u0069', '\u006A', '\u006B', '\u006C', '\u006D', '\u006E', '\u006F', 
            '\u0070', '\u0071', '\u0072', '\u0073', '\u0074', '\u0075', '\u0076', '\u0077', 
            '\u0078', '\u0079', '\u007A', '\u007B', '\u007C', '\u007D', '\u007E', '\u007F', 
            '\u1EA0', '\u1EAE', '\u1EB0', '\u1EB6', '\u1EA4', '\u1EA6', '\u1EA8', '\u1EAC', 
            '\u1EBC', '\u1EB8', '\u1EBE', '\u1EC0', '\u1EC2', '\u1EC4', '\u1EC6', '\u1ED0', 
            '\u1ED2', '\u1ED4', '\u1ED6', '\u1ED8', '\u1EE2', '\u1EDA', '\u1EDC', '\u1EDE', 
            '\u1ECA', '\u1ECE', '\u1ECC', '\u1EC8', '\u1EE6', '\u0168', '\u1EE4', '\u1EF2', 
            '\u00D5', '\u1EAF', '\u1EB1', '\u1EB7', '\u1EA5', '\u1EA7', '\u1EA9', '\u1EAD', 
            '\u1EBD', '\u1EB9', '\u1EBF', '\u1EC1', '\u1EC3', '\u1EC5', '\u1EC7', '\u1ED1', 
            '\u1ED3', '\u1ED5', '\u1ED7', '\u1EE0', '\u01A0', '\u1ED9', '\u1EDD', '\u1EDF', 
            '\u1ECB', '\u1EF0', '\u1EE8', '\u1EEA', '\u1EEC', '\u01A1', '\u1EDB', '\u01AF', 
            '\u00C0', '\u00C1', '\u00C2', '\u00C3', '\u1EA2', '\u0102', '\u1EB3', '\u1EB5', 
            '\u00C8', '\u00C9', '\u00CA', '\u1EBA', '\u00CC', '\u00CD', '\u0128', '\u1EF3', 
            '\u0110', '\u1EE9', '\u00D2', '\u00D3', '\u00D4', '\u1EA1', '\u1EF7', '\u1EEB', 
            '\u1EED', '\u00D9', '\u00DA', '\u1EF9', '\u1EF5', '\u00DD', '\u1EE1', '\u01B0', 
            '\u00E0', '\u00E1', '\u00E2', '\u00E3', '\u1EA3', '\u0103', '\u1EEF', '\u1EAB', 
            '\u00E8', '\u00E9', '\u00EA', '\u1EBB', '\u00EC', '\u00ED', '\u0129', '\u1EC9', 
            '\u0111', '\u1EF1', '\u00F2', '\u00F3', '\u00F4', '\u00F5', '\u1ECF', '\u1ECD', 
            '\u1EE5', '\u00F9', '\u00FA', '\u0169', '\u1EE7', '\u00FD', '\u1EE3', '\u1EEE', 
        });

        private VISCIIDecoder decoder;
        private VISCIIEncoder encoder;

        /// <summary>
        /// This should be thread safe. The worst case is that two instances
        /// of VISCIIDecoder are created at the same time, but this isn't
        /// a problem, because VISCIIDecoder as used in this class is 
        /// stateless.
        /// </summary>
        protected VISCIIDecoder Decoder
        {
            get
            {
                VISCIIDecoder decoder2 = decoder;

                // Lazy creation of Encoder
                if (object.ReferenceEquals(decoder2, null))
                {
                    decoder2 = decoder = new VISCIIDecoder();
                }

                DecoderFallback decoderFallback = DecoderFallback;

                // If the Fallback has changed from the last call, update it
                if (!object.ReferenceEquals(decoderFallback, null) && !object.ReferenceEquals(decoderFallback, decoder2.Fallback))
                {
                    decoder2.Fallback = decoderFallback;
                }

                return decoder2;
            }
        }

        /// <summary>
        /// This should be thread safe. The worst case is that two instances
        /// of VISCIIEncoder are created at the same time, but this isn't
        /// a problem, because VISCIIEncoder as used in this class is 
        /// stateless.
        /// </summary>
        protected VISCIIEncoder Encoder
        {
            get
            {
                VISCIIEncoder encoder2 = encoder;

                // Lazy creation of Encoder
                if (object.ReferenceEquals(encoder2, null))
                {
                    encoder = encoder2 = new VISCIIEncoder();
                }

                EncoderFallback encoderFallback = EncoderFallback;

                // If the Fallback has changed from the last call, update it
                if (!object.ReferenceEquals(encoderFallback, null) && !object.ReferenceEquals(encoderFallback, encoder2.Fallback))
                {
                    encoder2.Fallback = encoderFallback;
                }

                return encoder2;
            }
        }

        public override string BodyName
        {
            get
            {
                return "viscii-simple";
            }
        }

        public override string EncodingName
        {
            get
            {
                return BodyName;
            }
        }

        public override bool IsSingleByte
        {
            get
            {
                return true;
            }
        }

        public override object Clone()
        {
            var encoding = (VISCIIEncodingSimple)base.Clone();

            // We reset the encoder and decoder of the cloned instance,
            // because otherwise they would be shared between the two
            // instances.
            encoding.decoder = null;
            encoding.encoder = null;

            return encoding;
        }

        public override Decoder GetDecoder()
        {
            return new VISCIIDecoder();
        }

        public override Encoder GetEncoder()
        {
            return new VISCIIEncoder();
        }

        public override int GetByteCount(char[] chars, int index, int count)
        {
            return Encoder.GetByteCount(chars, index, count, true);
        }

        public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex)
        {
            return Encoder.GetBytes(chars, charIndex, charCount, bytes, byteIndex, true);
        }

        public override int GetCharCount(byte[] bytes, int index, int count)
        {
            return Decoder.GetCharCount(bytes, index, count, true);
        }

        public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex)
        {
            return Decoder.GetChars(bytes, byteIndex, byteCount, chars, charIndex, true);
        }

        public override int GetMaxByteCount(int charCount)
        {
            return charCount;
        }

        public override int GetMaxCharCount(int byteCount)
        {
            return byteCount;
        }
    }

    /// <summary>
    /// Fully thread safe/reentrant.
    /// </summary>
    public class VISCIIDecoder : Decoder
    {
        private static readonly char[] Unicodes = VISCIIEncodingSimple.Unicodes.ToArray();

        public override int GetCharCount(byte[] bytes, int index, int count)
        {
            if (bytes == null)
            {
                throw new ArgumentNullException("bytes");
            }

            if (index < 0 || index > bytes.Length)
            {
                throw new ArgumentOutOfRangeException("index");
            }

            if (count < 0)
            {
                throw new ArgumentOutOfRangeException("count");
            }

            if (index + count > bytes.Length)
            {
                throw new ArgumentOutOfRangeException("bytes");
            }

            return count;
        }

        public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex)
        {
            if (bytes == null)
            {
                throw new ArgumentNullException("bytes");
            }

            if (byteIndex < 0 || byteIndex > bytes.Length)
            {
                throw new ArgumentOutOfRangeException("byteIndex");
            }

            if (byteCount < 0)
            {
                throw new ArgumentOutOfRangeException("byteCount");
            }

            if (byteIndex + byteCount > bytes.Length)
            {
                throw new ArgumentOutOfRangeException("bytes");
            }

            if (chars == null)
            {
                throw new ArgumentNullException("chars");
            }

            if (charIndex < 0 || charIndex > chars.Length)
            {
                throw new ArgumentOutOfRangeException("charIndex");
            }

            int byteCount2 = byteCount + byteIndex;
            int charIndex2 = charIndex;

            for (; byteIndex < byteCount2; byteIndex++)
            {
                byte b = bytes[byteIndex];

                if (charIndex2 == chars.Length)
                {
                    throw new ArgumentException("chars");
                }

                // chars between 31 and 127 are equal in Unicode and VISCII
                chars[charIndex2] = b >= 31 && b <= 127 ? (char)b : Unicodes[b];
                charIndex2++;
            }

            return charIndex2 - charIndex;
        }
    }

    /// <summary>
    /// An instance is thread safe/fully reentrant if the methods are always
    /// called with flush = true.
    /// </summary>
    public class VISCIIEncoder : Encoder
    {
        private static readonly byte[] VISCIIs;

        // Buffer for High/Low surrogates. Note that this property is read
        // but not written if the methods are always used with flush = true.
        protected char HighSurrogate { get; set; }

        static VISCIIEncoder()
        {
            VISCIIs = new byte[1 + VISCIIEncodingSimple.Unicodes.Max()];

            for (int i = 0; i < VISCIIEncodingSimple.Unicodes.Count; i++)
            {
                VISCIIs[VISCIIEncodingSimple.Unicodes[i]] = (byte)i;
            }
        }

        public override int GetByteCount(char[] chars, int index, int count, bool flush)
        {
            if (chars == null)
            {
                throw new ArgumentNullException("chars");
            }

            if (index < 0 || index > chars.Length)
            {
                throw new ArgumentOutOfRangeException("index");
            }

            if (count < 0)
            {
                throw new ArgumentOutOfRangeException("count");
            }

            if (index + count > chars.Length)
            {
                throw new ArgumentOutOfRangeException("chars");
            }

            // The fallbackBuffer is created on-demand. The instance 
            // FallbackBuffer isn't used because it wouldn't be thread safe.
            EncoderFallbackBuffer fallbackBuffer = null;

            char highSurrogate = HighSurrogate;

            int ret = 0;

            int count2 = index + count;

            for (; index < count2; index++)
            {
                char ch = chars[index];

                if (highSurrogate != 0)
                {
                    if (fallbackBuffer == null)
                    {
                        fallbackBuffer = (Fallback ?? EncoderFallback.ReplacementFallback).CreateFallbackBuffer();
                    }

                    // If we have a High/Low surrogates couple, we pass them 
                    // together
                    if (char.IsLowSurrogate(ch))
                    {
                        if (fallbackBuffer.Fallback(highSurrogate, ch, index - 1))
                        {
                            HandleFallbackCount(fallbackBuffer, ref ret);
                        }

                        highSurrogate = '\0';

                        continue;
                    }
                    else
                    {
                        // First we pass the High surrogate to the Fallback
                        if (fallbackBuffer.Fallback(highSurrogate, index - 1))
                        {
                            HandleFallbackCount(fallbackBuffer, ref ret);
                        }

                        highSurrogate = '\0';

                        // Then we fall-through normal handling
                    }
                }

                if (ch < VISCIIs.Length && (VISCIIs[ch] != 0 || ch == '\0'))
                {
                    ret++;
                }
                else
                {
                    // High/low surrogate handling, done through buffer
                    if (char.IsHighSurrogate(ch))
                    {
                        highSurrogate = ch;
                    }
                    else
                    {
                        if (fallbackBuffer == null)
                        {
                            fallbackBuffer = (Fallback ?? EncoderFallback.ReplacementFallback).CreateFallbackBuffer();
                        }

                        // Fallback
                        if (fallbackBuffer.Fallback(ch, index))
                        {
                            HandleFallbackCount(fallbackBuffer, ref ret);
                        }
                    }
                }
            }

            if (flush)
            {
                if (highSurrogate != 0)
                {
                    if (fallbackBuffer == null)
                    {
                        fallbackBuffer = (Fallback ?? EncoderFallback.ReplacementFallback).CreateFallbackBuffer();
                    }

                    if (fallbackBuffer.Fallback(highSurrogate, index - 1))
                    {
                        HandleFallbackCount(fallbackBuffer, ref ret);
                    }
                }
            }

            return ret;
        }

        public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex, bool flush)
        {
            if (chars == null)
            {
                throw new ArgumentNullException("chars");
            }

            if (charIndex < 0 || charIndex > chars.Length)
            {
                throw new ArgumentOutOfRangeException("charIndex");
            }

            if (charCount < 0)
            {
                throw new ArgumentOutOfRangeException("charCount");
            }

            if (charIndex + charCount > chars.Length)
            {
                throw new ArgumentOutOfRangeException("chars");
            }

            if (bytes == null)
            {
                throw new ArgumentNullException("bytes");
            }

            if (byteIndex < 0 || byteIndex > bytes.Length)
            {
                throw new ArgumentOutOfRangeException("byteIndex");
            }

            // The fallbackBuffer is created on-demand. The instance 
            // FallbackBuffer isn't used because it wouldn't be thread safe.
            EncoderFallbackBuffer fallbackBuffer = null;

            // Written only on flush = false
            char highSurrogate = HighSurrogate;

            int charCount2 = charIndex + charCount;
            int byteIndex2 = byteIndex;

            for (; charIndex < charCount2; charIndex++)
            {
                char ch = chars[charIndex];

                if (highSurrogate != 0)
                {
                    if (fallbackBuffer == null)
                    {
                        fallbackBuffer = (Fallback ?? EncoderFallback.ReplacementFallback).CreateFallbackBuffer();
                    }

                    // If we have a High/Low surrogates couple, we pass them 
                    // together
                    if (char.IsLowSurrogate(ch))
                    {
                        if (fallbackBuffer.Fallback(highSurrogate, ch, charIndex - 1))
                        {
                            HandleFallbackWrite(fallbackBuffer, bytes, ref byteIndex2);
                        }

                        highSurrogate = '\0';

                        continue;
                    }
                    else
                    {
                        // First we pass the High surrogate to the Fallback
                        if (fallbackBuffer.Fallback(highSurrogate, charIndex - 1))
                        {
                            HandleFallbackWrite(fallbackBuffer, bytes, ref byteIndex2);
                        }

                        highSurrogate = '\0';

                        // Then we fall-through normal handling
                    }
                }

                byte b;

                if (ch < VISCIIs.Length && ((b = VISCIIs[ch]) != 0 || ch == '\0'))
                {
                    // Recognized character
                    WriteByte(bytes, byteIndex2, b);
                    byteIndex2++;
                }
                else
                {
                    // High/low surrogate handling, done through buffer
                    if (char.IsHighSurrogate(ch))
                    {
                        highSurrogate = ch;
                    }
                    else
                    {
                        if (fallbackBuffer == null)
                        {
                            fallbackBuffer = (Fallback ?? EncoderFallback.ReplacementFallback).CreateFallbackBuffer();
                        }

                        // Fallback
                        if (fallbackBuffer.Fallback(ch, charIndex))
                        {
                            HandleFallbackWrite(fallbackBuffer, bytes, ref byteIndex2);
                        }
                    }
                }
            }

            if (flush)
            {
                if (highSurrogate != 0)
                {
                    if (fallbackBuffer == null)
                    {
                        fallbackBuffer = (Fallback ?? EncoderFallback.ReplacementFallback).CreateFallbackBuffer();
                    }

                    if (fallbackBuffer.Fallback(highSurrogate, charIndex - 1))
                    {
                        HandleFallbackWrite(fallbackBuffer, bytes, ref byteIndex2);
                    }
                }
            }
            else
            {
                HighSurrogate = highSurrogate;
            }

            return byteIndex2 - byteIndex;
        }

        protected static void HandleFallbackCount(EncoderFallbackBuffer fallbackBuffer, ref int count)
        {
            while (fallbackBuffer.Remaining > 0)
            {
                char ch = fallbackBuffer.GetNextChar();

                if (!(ch < VISCIIs.Length && (VISCIIs[ch] != 0 || ch == '\0')))
                {
                    throw new EncoderFallbackException();
                }

                count++;
            }
        }

        protected static void HandleFallbackWrite(EncoderFallbackBuffer fallbackBuffer, byte[] bytes, ref int byteIndex)
        {
            while (fallbackBuffer.Remaining > 0)
            {
                char ch = fallbackBuffer.GetNextChar();

                byte b;

                if (!(ch < VISCIIs.Length && ((b = VISCIIs[ch]) != 0 || ch == '\0')))
                {
                    throw new EncoderFallbackException();
                }

                WriteByte(bytes, byteIndex, b);
                byteIndex++;
            }
        }

        // Remove the next line if using .NET < 4.5
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        protected static void WriteByte(byte[] bytes, int byteIndex, byte b)
        {
            if (byteIndex == bytes.Length)
            {
                throw new ArgumentException("bytes");
            }

            bytes[byteIndex] = b;
        }
    }
}

像这样使用它:

{
    var decoder = new VISCIIEncodingSimple.VISCIIDecoder();
    var encoder = new VISCIIEncodingSimple.VISCIIEncoder();

    // Some characters that are part of the VISCII character set/aren't part of the VISCII character set
    // Illegal characters are replaced with ?
    string str = "OK:ABCD1234!~|OK:\x00\x01\x03\x04\x7F|OK:ẲẴẪỶỸỴẠỮ|KO:\x02\x05\x06§|KO:\U0001F602|KO:\U000100000|KO:\uD800\uD800|KO:\uDBFF\uDBFF|KO:\uD800";
    char[] chars = str.ToCharArray();
    var byteCount = encoder.GetByteCount(chars, 0, chars.Length, true);
    var bytes = new byte[byteCount];
    var res = encoder.GetBytes(chars, 0, chars.Length, bytes, 0, true);
    int charCount = decoder.GetCharCount(bytes, 0, bytes.Length);
    var chars2 = new char[charCount];
    var res2 = decoder.GetChars(bytes, 0, bytes.Length, chars2, 0, true);
    var str2 = new string(chars2);
}

{
    var encoding = new VISCIIEncodingSimple();

    // Some characters that are part of the VISCII character set/aren't part of the VISCII character set
    // Illegal characters are replaced with ?
    string str = "OK:ABCD1234!~|OK:\x00\x01\x03\x04\x7F|OK:ẲẴẪỶỸỴẠỮ|KO:\x02\x05\x06§|KO:\U0001F602|KO:\U000100000|KO:\uD800\uD800|KO:\uDBFF\uDBFF|KO:\uD800";
    var bytes = encoding.GetBytes(str);
    var str2 = encoding.GetString(bytes);
}

基于http://en.wikipedia.org/wiki/Vietnamese_Standard_Code_for_Information_Interchange中的表格

请注意,这是我的第一个 Encoder/Decoder 子类,所以我不能 100% 确定我做了所有应该做的事情 :-)

在您的情况下,您应该可以直接传递为:

port.Encoding = new VISCIIEncodingSimple();

【讨论】: