1

もちろん、これはエンディアンと関係があることを私は知っています。Javaはビッグエンディアンであり、C#はリトルエンディアンであることを私は知っています。

文字列を外部システムに送信するJavaで動作するプログラムがあります。重要なのは、文字列の長さを表す文字列の先頭で2バイトのヘッダーを使用する必要があるということです。

この2バイトのヘッダーでは、最初のバイトは文字列が255文字を超えているかどうかを示すだけで、超えていない場合は常に0です。ただし、2番目のバイト(C#では正しく取得できないバイト)は文字列の長さ。最初のバイトが0の場合(ほとんどすべての場合)、文字列の長さ全体を表します。たとえば、226とします。

したがって、Javaには、ヘッダーを計算して元の文字列を追加するこのコードセグメントがあり、送信する最終的な文字列を取得できます。これは問題なく機能します。

コードのJava作業セグメント:

String iso_str = iso_buf.toString();    // This contains original string without header

int iso_len = iso_str.length();     // We need to know if it exceeds 255

int i, j;
i = (int)(iso_len / 256);           // Always 0. It would be 1 if original string bigger than 255
j = (int)(iso_len % 256);           // Length of the string (most times 226)

Integer ii = new Integer(i);
Integer jj = new Integer(j);

byte[] bmsg_0200 = new byte[iso_len + 2];   // We create an array of bytes making room for the 2 bytes header and the original string
bmsg_0200[0] = ii.byteValue();      // Header byte number one
bmsg_0200[1] = jj.byteValue();      // Header byte number two

System.arraycopy(iso_str.getBytes(), 0, bmsg_0200, 2, iso_str.length()); // Then we just copy the original string in the array after the header         

String mensaje_str = new String(bmsg_0200); // Make the whole byte array into one string to send

次のような部分で:bmsg_0200 [1] = jj.byteValue(); ここで、javaは実際に機能するバイト値を取得します(jjが226の場合は-30になります)。そして、最終的なシステムはこのヘッダーを理解するため、すべてのメッセージを読み取ります。

.NET(C#)でコードを複製しようとしましたが、次のコードがあります。

コードのC#が機能しないセグメント:

int tramaISOLongitud = iso.Length;  // iso contains original string without header, We need to know if it exceeds 255

int i, j;
i = (int)(tramaISOLongitud / 256);  // Always 0. It would be 1 if original string bigger than 255
j = (int)(tramaISOLongitud % 256);  // Length of the string (most times 226)            

byte[] tramaISOBytes = new byte[tramaISOLongitud + 2];  // We create an array of bytes making room for the 2 bytes header and the original string
tramaISOBytes[0] = Convert.ToByte(i);   // Header byte number one
tramaISOBytes[1] = Convert.ToByte(j);   // Header byte number two

System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
System.Array.ConstrainedCopy(encoding.GetBytes(iso), 0, tramaISOBytes, 2, tramaISOLongitud); // Then we just copy the original string in the array after the header

System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding(); 
string tramaISOHeader = enc.GetString(tramaISOBytes); // Make the whole byte array into one string to send

最後のコードはすべてをコンパイルしますが、tramaISOBytes [1] = Convert.ToByte(j);で間違ったバイトを取得します。リトルエンディアンだからです。だから私は次のようにSystem.Net.IPAddress.HostToNetworkOrderを使用してみました:

int tramaISOLongitud = iso.Length;  // iso contains original string without header, We need to know if it exceeds 255

int i, j;
i = (int)(tramaISOLongitud / 256);  // Always 0. It would be 1 if original string bigger than 255
j = (int)(tramaISOLongitud % 256);  // Length of the string (most times 226)    

int i2 = System.Net.IPAddress.HostToNetworkOrder(i);
int j2 = System.Net.IPAddress.HostToNetworkOrder(j);    

byte[] tramaISOBytes = new byte[tramaISOLongitud + 2];  // We create an array of bytes making room for the 2 bytes header and the original string
tramaISOBytes[0] = Convert.ToByte(i2);  // Header byte number one
tramaISOBytes[1] = Convert.ToByte(j2);  // Header byte number two

System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
System.Array.ConstrainedCopy(encoding.GetBytes(iso), 0, tramaISOBytes, 2, tramaISOLongitud); // Then we just copy the original string in the array after the header

System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding(); 
string tramaISOHeader = enc.GetString(tramaISOBytes); // Make the whole byte array into one string to send

しかし、j2は巨大な数(-503316480など)を取得しますが、大きすぎるために1バイトに変換できません。また、前述のヘッダーの長さが2バイトしかないため、1バイトで必要になります。何か案は?

前もって感謝します。


OK私はこれをもっと簡単な方法で説明します:

C#の場合:

int test    = -10471344;
int test2   = -503316480;
int test3 = 57856;
byte b = (byte)test;
byte b2 = (byte)test2;
byte b3 = (byte)test3;

これらの行を実行すると、次のようになります。b = 80 b2 = 0 b3 = 0

したがって、(バイト)にキャストされた数値-10471344が80になるのはなぜですか。そして他のものはちょうど0を与えますか?実際、私は変数test2 = -503316480に興味があります。それを別の変数に変換したいのですが、0を取得します。なぜbを80にし、他の変数を0以外にすることはできないのでしょうか。


解決策:ジムが言ったように、私の問題はエンディアンとは何の関係もありませんでしたが(それはそれに関連するすべてだと思っていましたが)、重要な知識は、.NETの世界では文字列をバイト配列に変換することは問題ではないということです。ただし、バイト配列から文字列に戻ると、エンコーディング(ASCIIEncodingまたはUTF8Enconding)を使用する必要があるため問題が発生し、バイトが破損するために問題が発生します。

Javaでは、この種の問題はありませんでした。

つまり、これはその仕事をしたコードのブロックです:

static byte[] addHeader2(string iso)
{
    int tramaISOLongitud = iso.Length;

    byte highByte = (byte)(tramaISOLongitud >> 8);
    byte lowByte = (byte)(tramaISOLongitud & 255);

    byte[] tramaISOBytes = new byte[tramaISOLongitud + 2];
    tramaISOBytes[0] = highByte;
    tramaISOBytes[1] = lowByte;

    System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
    System.Array.ConstrainedCopy(encoding.GetBytes(iso), 0, tramaISOBytes, 2, tramaISOLongitud);

    return tramaISOBytes;
}

そして、そのバイト配列をそのままソケットに送信します。

あなたの助けをありがとう!

4

2 に答える 2

2

あなたの問題はエンディアンとは何の関係もありません。算術演算を行う場合、システムがビッグエンディアンであるかリトルインディアンであるかは関係ありません。つまり、これを考えると:

int len = 260;

byte highByte = (byte)(len >> 8);
byte lowByte = (byte)(len & 255);

Console.WriteLine("highByte = {0:X2}", highByte);
Console.WriteLine("lowByte = {0:X2}", lowByte);

highByte常に1になり、常に4lowByteになります。

HostToNetWorkOrder変換を行う必要はありません。

これで、負の値を使用している場合、除算演算子とmod演算子によって予期しない結果が得られます。しかし、intに格納しているのは2バイト長の値であるとおっしゃっていたので、負の値でも問題はありません。

tramaISOBytes元のC#コードをシングルステップで実行すると、配列の最初の2つの位置に正しい値が格納されていることがわかります。結果のバイト配列を文字列に変換しようとすると、問題が発生するのではないかと思います。を使用してASCIIEncoding、バイトの配列を文字列に変換します。これにより、127(0x7Fh)より大きいバイトがマングルされます。

したがって、コンバージョンではなく、次のようになります。

System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
string tramaISOHeader = enc.GetString(tramaISOBytes);

8ビットエンコーディングを使用したい。おそらく、ISO-8859-2のようなものです。基本的に、変換を試みないものが必要です。または、さらに良いことに、これを送信用にまとめる場合は、文字列に戻すことすらすべきではありません。バイト配列を直接送信するだけです。そうすれば、物事が混乱する可能性はありません。

于 2012-04-12T17:01:49.877 に答える
0

2バイトをバイト配列にコピーし、バイトをushortに変換してみてください。

bool remoteSystemIsLittleEndian = false;
byte[] stringLengthBytes = new byte[] { 1, 255 };

if (BitConverter.IsLittleEndian != remoteSystemIsLittleEndian)
{
    Array.Reverse(stringLengthBytes);
}

int stringLength = (int)BitConverter.ToUInt16(stringLengthBytes, 0);

そして、これは数値を2バイト配列に変換するためのものです。

bool remoteSystemIsLittleEndian = false;
int stringLength = 511;

byte[] stringLengthBytes = BitConverter.GetBytes((ushort)stringLength);

if (BitConverter.IsLittleEndian != remoteSystemIsLittleEndian)
{
    Array.Reverse(stringLengthBytes);
}
于 2012-04-12T15:49:51.347 に答える