5

だから私はいくつかの例を見つけましたが、私が望むものとまったく同じものはありません(閉じます)

私が探しているものの例

byte[] array = { 0x02, 0x64, 0x40, 0x40, 0x03 };
string text = SomeMagicalMethod(array);
//displays <STX>FOO<ETX>

ある時点で、印刷できない文字の値と <...> を含む辞書がありましたが、2 つの文字列を組み合わせると頭が痛くなります。私は現在これを持っていますが、ASCIIEncoding はすべての制御文字を取り出します。

public static void Add(this TextBox tb, byte[] array)
{
  string input = System.Text.ASCIIEncoding.ASCII.GetString(array);

  Regex.Replace( input     ,
                 @"\p{Cc}" ,
                 a => string.Format( "[{0:X2}]" , (byte)a.Value[0] )
               ) ;
  Add(tb, input);

}

public static void Add(this TextBox tb, string text)
{
  text += ENTER;
  tb.Dispatcher.Invoke( DispatcherPriority.Background,
    new Action(delegate() { tb.Text += text; })
    );
}

EDIT NUnitを使用して、これらのテストに対して回答されたコードを実行しました。最後の値には、0x7F の範囲外の値が 1 つあります。これが使用されるコードにはそれが含まれていてはなりませんが、安全を確保してから後悔することをお勧めします。

[TestFixture]
public class StringExtensionsTest
{
    [Test]
    public void SingleByteControlCharacterTest()
    {
        AssertSingleByte(0x00, "<NUL>"); AssertSingleByte(0x01, "<SOH>");
        AssertSingleByte(0x02, "<STX>"); AssertSingleByte(0x03, "<ETX>");
        AssertSingleByte(0x04, "<EOT>"); AssertSingleByte(0x05, "<ENQ>");
        AssertSingleByte(0x06, "<ACK>"); AssertSingleByte(0x07, "<BEL>");
        AssertSingleByte(0x08, "<BS>" ); AssertSingleByte(0x09, "<HT>" );
        AssertSingleByte(0x0A, "<LF>" ); AssertSingleByte(0x0B, "<VT>" );
        AssertSingleByte(0x0C, "<FF>" ); AssertSingleByte(0x0D, "<CR>" );
        AssertSingleByte(0x0E, "<SO>" ); AssertSingleByte(0x0F, "<SI>" );

        AssertSingleByte(0x10, "<DLE>"); AssertSingleByte(0x11, "<DC1>");
        AssertSingleByte(0x12, "<DC2>"); AssertSingleByte(0x13, "<DC3>");
        AssertSingleByte(0x14, "<DC4>"); AssertSingleByte(0x15, "<NAK>");
        AssertSingleByte(0x16, "<SYN>"); AssertSingleByte(0x17, "<ETB>");
        AssertSingleByte(0x18, "<CAN>"); AssertSingleByte(0x19, "<EM>" );
        AssertSingleByte(0x1A, "<SUB>"); AssertSingleByte(0x1B, "<ESC>");
        AssertSingleByte(0x1C, "<FS>" ); AssertSingleByte(0x1D, "<GS>" );
        AssertSingleByte(0x1E, "<RS>" ); AssertSingleByte(0x1F, "<US>" );
        AssertSingleByte(0x7F, "<DEL>");
    }
    private void AssertSingleByte(byte value, string expected)
    {
        byte[] array = new byte[]{value};
        var actual = array.asciiOctets2String();
        Assert.AreEqual(expected, actual, "Didn't print the epxected result");
    }

    [Test]
    public void SingleCharacterTest()
    {
        for (byte i = 0x20; i < 0x7F; i++) 
        {
            AssertSingleByte(i, char.ToString((char)i));
        }
    }

    [Test]
    public void SimpleTestTogether()
    {
        byte[] array = {0x02, 0x46,0x4F, 0x4F, 0x03};
        string expected = "<STX>FOO<ETX>";
        string actual = array.asciiOctets2String();
        Assert.AreEqual(expected, actual, "Simple test failed");
    }
    [Test]
    public void BigTest()
    {
        byte[] array = {
            0x00, 0x7F, 0x03, 0x52, 0x00, 0x00, 0x2F, 0x5F, 0x20, 0x0F, 0x43, 0x41, 0x52, 0x44, 0x48, 0x4F,
            0x4C, 0x44, 0x45, 0x52, 0x2F, 0x56, 0x49, 0x53, 0x41, 0x9F, 0x1F, 0x07, 0x30, 0x30, 0x30, 0x30,
            0x30, 0x30, 0x30};
        string expected = "<NUL><DEL><ETX>R<NUL><NUL>/_ <SI>CARDHOLDER/VISA?<US><BEL>0000000";
        string actual = array.asciiOctets2String();

        Assert.AreEqual(expected, actual, "BigTest Failed");
    }
}
4

2 に答える 2

7

制御文字をどうしたいかのテーブルを作成する必要があります。たとえば、次のようになります。

Dictionary<byte, string> controlCodes = new Dictionary<byte, string>
{
    { 0x00, "<NUL>" },
    { 0x01, "<SOH>" },
    { 0x02, "<STX>" },
    { 0x03, "<ETX>" },
    ...
}

このウィキペディアのページを使用して、完全なリストを作成できます。

次に、ビルドは次のように簡単です。

var output = String.Join(
    string.Empty, 
    array.Select(b => 
        controlCodes.ContainsKey(b) 
        ? controlCodes[b]
        : Encoding.ASCII.GetString(new[] { b })));

もちろん、これは大幅に改善される可能性があります。このようなものはもう少し効率的です:

string str = string.Empty;
int i = 0, j = 0;
while (i < array.Length && j > -1)
{
    j = Array.FindIndex(array, i, b => controlCodes.ContainsKey(b));
    if (j > -1) 
    {
        if (j > i)
        {
            str += Encoding.ASCII.GetString(array, i, j - i);
        }

        str += controlCodes[array[j]];
        i = j + 1;
    }
    else 
    {
        str += Encoding.ASCII.GetString(array, i, array.Length - i);
    }
}

return str;
于 2013-08-28T21:15:23.167 に答える
4

C# ASCII コンバーターは、7 ビット ASCII 範囲 (0x00 ~ 0x7F) 以外の文字をジャンクします。

その範囲外のコード ポイントがある場合、ASCII はありません。適切なエンコーディング (UTF-8 など) を使用してください。

バイト配列が ASCII (コード ポイント 0x00 ~ 0x7Fのみを含む) であることがわかっている場合は、コンバーターはまったく必要ありません。このようなことができます:

static string asciiOctets2String( byte[] bytes )
{
  StringBuilder sb = new StringBuilder(bytes.Length);
  foreach ( char c in bytes.Select( b => (char) b ) )
  {
    switch ( c )
    {
    case '\u0000' : sb.Append("<NUL>") ; break ;
    case '\u0001' : sb.Append("<SOH>") ; break ;
    case '\u0002' : sb.Append("<STX>") ; break ;
    case '\u0003' : sb.Append("<ETX>") ; break ;
    case '\u0004' : sb.Append("<EOT>") ; break ;
    case '\u0005' : sb.Append("<ENQ>") ; break ;
    case '\u0006' : sb.Append("<ACK>") ; break ;
    case '\u0007' : sb.Append("<BEL>") ; break ;
    case '\u0008' : sb.Append("<BS>" ) ; break ;
    case '\u0009' : sb.Append("<HT>" ) ; break ;
    case '\u000A' : sb.Append("<LF>" ) ; break ;
    case '\u000B' : sb.Append("<VT>" ) ; break ;
    case '\u000C' : sb.Append("<FF>" ) ; break ;
    case '\u000D' : sb.Append("<CR>" ) ; break ;
    case '\u000E' : sb.Append("<SO>" ) ; break ;
    case '\u000F' : sb.Append("<SI>" ) ; break ;
    case '\u0010' : sb.Append("<DLE>") ; break ;
    case '\u0011' : sb.Append("<DC1>") ; break ;
    case '\u0012' : sb.Append("<DC2>") ; break ;
    case '\u0013' : sb.Append("<DC3>") ; break ;
    case '\u0014' : sb.Append("<DC4>") ; break ;
    case '\u0015' : sb.Append("<NAK>") ; break ;
    case '\u0016' : sb.Append("<SYN>") ; break ;
    case '\u0017' : sb.Append("<ETB>") ; break ;
    case '\u0018' : sb.Append("<CAN>") ; break ;
    case '\u0019' : sb.Append("<EM>" ) ; break ;
    case '\u001A' : sb.Append("<SUB>") ; break ;
    case '\u001B' : sb.Append("<ESC>") ; break ;
    case '\u001C' : sb.Append("<FS>" ) ; break ;
    case '\u001D' : sb.Append("<GS>" ) ; break ;
    case '\u001E' : sb.Append("<RS>" ) ; break ;
    case '\u001F' : sb.Append("<US>" ) ; break ;
    case '\u007F' : sb.Append("<DEL>") ; break ;
    default :
      if ( c > '\u007F' )
      {
        sb.AppendFormat( @"\u{0:X4}" , (ushort)c ) ; // in ASCII, any octet in the range 0x80-0xFF doesn't have a character glyph associated with it
      }
      else
      {
        sb.Append( c ) ;
      }
      break ;
    }
  }
  return sb.ToString() ;
}

または、おそらくスイッチよりも高速で、おそらく辞書ベースのアプローチよりも高速ですが、読みにくい別のアプローチ:

private static string[] controlChars =
{
  "<NUL>" , "<SOH>" , "<STX>" , "<ETX>" ,
  "<EOT>" , "<ENQ>" , "<ACK>" , "<BEL>" ,
  "<BS>"  , "<HT>"  , "<LF>"  , "<VT>"  ,
  "<FF>"  , "<CR>"  , "<SO>"  , "<SI>"  ,
  "<DLE>" , "<DC1>" , "<DC2>" , "<DC3>" ,
  "<DC4>" , "<NAK>" , "<SYN>" , "<ETB>" ,
  "<CAN>" , "<EM>"  , "<SUB>" , "<ESC>" ,
  "<FS>"  , "<GS>"  , "<RS>"  , "<US>"  ,
} ;
static string asciiOctets2String( byte[] bytes )
{
  StringBuilder sb = new StringBuilder(bytes.Length);
  foreach ( char c in bytes.Select( b => (char) b ) )
  {
    if      ( c <  '\u0020' ) { sb.Append( controlChars[c] ) ; }
    else if ( c == '\u007F' ) { sb.Append( "<DEL>"         ) ; }
    else if ( c >  '\u007F' ) { sb.AppendFormat( @"\u{0:X4}" , (ushort)c ) ; }
    else /* 0x20-0x7E */      { sb.Append( c ) ; }
  }
  return sb.ToString() ;
}
于 2013-08-28T21:48:17.753 に答える