Mittwoch, 29. Oktober 2008

Hexadezimaldarstellung eines Strings nach Int32 umrechnen

BBo hat mir eine Aufgabenstellung hingeworfen und wir haben diese dann unterschiedlich gelöst. Es ging darum die Hexadezimaldarstellung eines 2 Byte langen String nach Int32 zu konvertieren. Das war etwas schwieriger als wir dachten. Hier nun meine Lösungsvariante, vielleicht postet BBo ja noch seine ;)

Das Erste was mir durch den Kopf ging ist, dass dies etwas gefährlich ist in Bezug auf das Stringencoding. Denn je nach dem wie der String decodiert wurde, hat man ja je nach Umgebung eine andere Hexadezimalwertdarstellung nach dem Encodieren. Darum habe ich einfach zur Absicherung das Encoding mit eingebaut in meine Lösung. Theoretisch kann man in einer homogenen Welt darauf verzichten, doch meine praktischen Erfahrungen in den letzten Monaten haben mir oft genug die Finger verkokelt. Man kann ja selber mal damit herumspielen und mit unterschiedlichen Encodings die unterschiedlichen Ergebnisse vergleichen...

   1:  // test
   2:  int erg = TwoByteStringHexValueToInt32("Aÿ", System.Text.Encoding.GetEncoding(1252));
   3:  ...
   4:   
   5:  public static int TwoByteStringHexValueToInt32(string input, Encoding encoding)
   6:  {
   7:     if (input.Length != 2)
   8:        throw new ArgumentException("TwoByteStringHexValueToInt32 - parameter input is allowed to be 2 byte long only!");
   9:              
  10:     if (encoding == null)
  11:        encoding = System.Text.Encoding.Default;
  12:   
  13:     int result = 0;
  14:     byte[] ba = encoding.GetBytes(input); // split to byte array of 2
  15:   
  16:     if (System.BitConverter.IsLittleEndian) // when little endian we need to do a workaround
  17:     {
  18:        result = (Convert.ToInt32(ba[0]) << 8) + Convert.ToInt32(ba[1]);
  19:     }
  20:     else
  21:     {
  22:        result = (int)System.BitConverter.ToInt16(ba, 0);
  23:     }
  24:   
  25:     return result;
  26:  }

In Zeile 14 geht es richtig los, da zerleg ich den Input string in ein 2-Byte Array in Abhängigkeit (!) vom Encoding des Strings. 1252 ist das herkömmliche Encoding unter Windows für lateinische Schriftsysteme, u.a. Deutsch. Danach steht also in unserem Byte Array die Hexdarstellung unseres Strings.

Danach mache ich einen kleinen Kunstgriff in Zeile 16, x86 Syteme sind LittleEndian kodiert, Motorola z.B. normalerweise BigEndian. Hier eine kleine Info wenn man damit nichts anfangen kann. Im Klartext, normalerweise rechnet man den Wert in unserem Bytearray wie in Zeile 22 gezeigt um. Mit dem Bitkonverter nach 16Bit Integer, werden 2 Bytes aus dem Bytearray ab der Position 0 gelesen und in Integer umgerechnet (Int32 bräuchte 4 Bytes, darum dieser Umweg).

Und GENAU hier schlägt die Sache mit dem Little/Big Endian System zu! Denn wir haben zuvor oben unseren String eigentlich verkehrt herum abgelegt für unser x86 System!! Denn das rechnet von Hinten nach Vorne Bytedarstellungen nach Integer um!

Daher das Byteshifting in Zeile 18, falls ein Little Endian System vorliegt. Ich greife mir das erste Byte konvertiere es nach Int32 und verschiebe es mal 8 Bits nach "links" (= 1 Byte, wir haben ja 2 Bytes und schieben das Erste sozusagen nach "vorne"), um dann einfach das Ergebnis aus der zweiten Byte Konvertierung dazu addieren.

Nachtrag: Der Rückweg

   1:  public static byte[] Int16ToBigEndianByteArray(short input)
   2:  {
   3:     int higher = input >> 8;     // shift 1 byte -> get higher byte
   4:     int lower  = input & 0x00ff; // mask -> get lower byte
   5:   
   6:     byte[] ba = new byte[2];
   7:     ba[0] = Convert.ToByte(higher);
   8:     ba[1] = Convert.ToByte(lower);
   9:   
  10:     return ba;
  11:  }
  12:   
  13:   
  14:  ...
  15:  // test
  16:  byte[] ba = Int16ToBigEndianByteArray((short)31910);
  17:  string res = System.Text.Encoding.GetEncoding(1252).GetString(ba);

Keine Kommentare: