当然我知道这与endianess有关。我知道Java是big-endian而C#是little-endian。
我有一个Java工作程序,它将字符串发送到外部系统。问题是他们需要在表示其长度的字符串的开始处使用2字节的标头。
在这个2字节的标题中,第一个是告诉字符串是否超过255个字符,如果不是,它总是0.但第二个字节(这是我在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),它不能转换成单个字节因为太大而且我确实需要它在一个字节中,因为我说的标题只有2个字节长。有什么想法吗?
提前致谢。
好的我会以更简单的方式表达:
在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?
解决方案:正如Jim所说,我的问题与endianness没有任何关系(虽然我认为这是与此相关的一切),但关键知识是在.NET世界中将String转换为字节数组是没有问题,但是从字节数组回到字符串是你遇到问题的地方,因为你必须使用编码(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;
}
我只是通过套接字发送该字节数组。
感谢所有人的帮助!
答案 0 :(得分:2)
你的问题与endian-ness无关。当你进行算术运算时,系统是大端还是小印度并不重要。也就是说,鉴于此:
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,而lowByte
始终为4。
无需进行HostToNetWorkOrder转换。
现在,如果你正在使用负值,你的除法运算符和mod运算符会给你带来意想不到的结果。但你说它是一个2字节的长度值,你存储在一个int中,所以负值不应该给你一个问题。
我认为如果您单步执行原始C#代码,您将看到正确的值存储在tramaISOBytes
数组的前两个位置。我怀疑当您尝试将生成的字节数组转换为字符串时会出现问题。您正在使用ASCIIEncoding
将您的字节数组转换为字符串,这将破坏任何大于127(0x7Fh)的字节。
所以,而不是你的转换:
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
string tramaISOHeader = enc.GetString(tramaISOBytes);
您想使用8位编码。或许像ISO-8859-2这样的东西。基本上,你想要一些不会尝试进行任何转换的东西。或者,更好的是,如果你将它们放在一起进行传输,你甚至不应该转换回字符串。只是直接传输字节数组。这样就不会有事情被破坏了。
答案 1 :(得分: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);
}