Laden...

Klasse für das Erstellen eines HexStrings aus Byte[] und auch wieder zurück.

Erstellt von TiltonJH vor 14 Jahren Letzter Beitrag vor 14 Jahren 7.679 Views
TiltonJH Themenstarter:in
87 Beiträge seit 2006
vor 14 Jahren
Klasse für das Erstellen eines HexStrings aus Byte[] und auch wieder zurück.

Beschreibung:

Auf der Suche nach Klassen und Methoden zum Wandeln von Byte[] in einen HexString und auch wieder zurück bin ich immer wieder auf halbfertige oder optimierungsbedürftige Snippets gestoßen.

Also dann doch selber machen. 😁

Hier nun meine Lösung.

Falls jmd noch Optimierungspotential findet, immer her damit. =)


using System;
using System.Text;

namespace Tools.Strings
{
	/// <summary>
	/// Provides methods for woking with hexadecimal values.
	/// </summary>
	public static class Hexadecimal
	{
		/// <summary>
		/// Determines if the <see cref="Char"/> is a hex digit.
		/// </summary>
		/// <param name="digit">A <see cref="Char"/>.</param>
		/// <returns><see langword="true"/>, if the given <see cref="Char"/> is a valid hex digit; - otherwise <see langword="false"/>.</returns>
		public static bool IsValidHexDigit(char digit)
		{
			return digit >= 0x30 && digit <= 0x39;
		}
		/// <summary>
		/// Determines if the <see cref="Char"/> is a lower case hex letter.
		/// </summary>
		/// <param name="letter">A <see cref="Char"/>.</param>
		/// <returns><see langword="true"/>, if the given <see cref="Char"/> is a lower case hex letter; - otherwise <see langword="false"/>.</returns>
		public static bool IsValidLowerCaseHexLetter(char letter)
		{
			return letter >= 0x61 && letter <= 0x66;
		}
		/// <summary>
		/// Determines if the <see cref="Char"/> is a upper case hex letter.
		/// </summary>
		/// <param name="letter">A <see cref="Char"/>.</param>
		/// <returns><see langword="true"/>, if the given <see cref="Char"/> is a upper case hex letter; - otherwise <see langword="false"/>.</returns>
		public static bool IsValidUpperCaseHexLetter(char letter)
		{
			return letter >= 0x41 && letter <= 0x46;
		}
		/// <summary>
		/// Determines if the <see cref="Char"/> is a valid hex char.
		/// </summary>
		/// <param name="hexChar">A <see cref="Char"/>.</param>
		/// <returns><see langword="true"/>, if the given <see cref="Char"/> is a valid hex char; - otherwise <see langword="false"/>.</returns>
		public static bool IsValidHexChar(char hexChar)
		{
			return Hexadecimal.IsValidHexDigit(hexChar) ||
				Hexadecimal.IsValidLowerCaseHexLetter(hexChar) ||
				Hexadecimal.IsValidUpperCaseHexLetter(hexChar);
		}
		/// <summary>
		/// Determines if the <see cref="String"/> is a valid hexString.
		/// </summary>
		/// <param name="hexString">A <see cref="String"/>.</param>
		/// <returns><see langword="true"/>, if the given <see cref="String"/> is a valid hexString; - otherwise <see langword="false"/>.</returns>
		public static bool IsValidHexString(string hexString)
		{
			if (hexString == null || hexString.Length == 0)
				return false;

			int i = 0;
			for (; i < hexString.Length; i++)
			{
				if (hexString[i] != ' ')
					break;
			}
			if (i >= hexString.Length)
				return false;

			if (hexString.Length - i >= 2)
			{
				char one = hexString[i];
				char two = hexString[i + 1];
				if (one == '0' && (two == 'x' || two == 'X'))
					i += 2;
			}

			if (i >= hexString.Length)
				return false;

			while (i < hexString.Length)
			{
				char c = hexString[i++];
				if (!(Hexadecimal.IsValidHexChar(c)))
					return false;
			}

			return true;
		}
		/// <summary>
		/// Returns the <see cref="Byte"/> representation of an hexDigits.
		/// </summary>
		/// <param name="hexDigit">The hexDigit.</param>
		/// <returns>The <see cref="Byte"/> representation of an hexDigits.</returns>
		/// <exception cref="FormatException"><paramref name="hexDigit"/> is not a valid hexDigit.</exception>
		public static byte ToByte(char hexDigit)
		{
			if (Hexadecimal.IsValidHexDigit(hexDigit))
				return (byte)(hexDigit - 0x30);
			else if (Hexadecimal.IsValidLowerCaseHexLetter(hexDigit))
				return (byte)(hexDigit - 0x57);
			else if (Hexadecimal.IsValidUpperCaseHexLetter(hexDigit))
				return (byte)(hexDigit - 0x37);
			else
				throw new FormatException("Is not a valid hexDigit.");
		}
		/// <summary>
		/// Returns the <see cref="Byte"/> representation of two hexDigits.
		/// </summary>
		/// <param name="leftHexDigit">The left hexDigit.</param>
		/// <param name="rightHexDigit">The right hexDigit.</param>
		/// <returns>The <see cref="Byte"/> representation of two hexDigits.</returns>
		public static byte ToByte(char leftHexDigit, char rightHexDigit)
		{
			return (byte)(Hexadecimal.ToByte(leftHexDigit) << 4 | Hexadecimal.ToByte(rightHexDigit));
		}
		/// <summary>
		/// Converts a hexString into an array of <see cref="Byte"/>.
		/// </summary>
		/// <param name="hexString">A hexString to convert into an array of <see cref="Byte"/>.</param>
		/// <returns>An array of <see cref="Byte"/> representing the given <paramref name="hexString"/>.</returns>
		/// <exception cref="ArgumentNullException"><paramref name="hexString"/> is <see langword="null"/>.</exception>
		/// <exception cref="FormatException"><paramref name="hexString"/> is not a valid hexString.</exception>
		public static byte[] ToBytes(string hexString)
		{
			if (hexString == null)
				throw new ArgumentNullException("hexString");
			if (hexString.Length == 0)
				return new byte[0];

			try
			{
				int i = 0;
				for (; i < hexString.Length; i++)
				{
					if (hexString[i] != ' ')
						break;
				}
				if (i >= hexString.Length)
					return new byte[0];

				if (hexString.Length - i >= 2)
				{
					char one = hexString[i];
					char two = hexString[i + 1];
					if (one == '0' && (two == 'x' || two == 'X'))
						i += 2;
				}

				if (i >= hexString.Length)
					return new byte[0];

				bool odd = (hexString.Length & 1) == 1;

				int length = hexString.Length - i >> 1;

				if (odd)
					length++;

				byte[] bytes = new byte[length];
				int j = 0;

				if (odd)
					bytes[j++] = ToByte(hexString[i++]);

				while (i < hexString.Length)
				{
					bytes[j++] = ToByte(hexString[i++], hexString[i++]);
				}

				return bytes;
			}
			catch (Exception ex)
			{
				throw new FormatException("Is not a valid hex string.", ex);
			}
		}
		/// <summary>
		/// Returns a hex char.
		/// </summary>
		/// <param name="value">A <see cref="Int32"/> representing an hex char.</param>
		/// <returns>A <see cref="Char"/> representation of a given hex char.</returns>
		/// <exception cref="ArgumentOutOfRangeException"><paramref name="value"/> is not in range: 0 &lt;= value &lt;= 15</exception>
		public static char ToHexChar(int value)
		{
			if (value >= 0 && value <= 9)
				return (char)(value + 0x30);
			else if (value > 9 && value <= 15)
				//lower case 0x57
				//upper case 0x37
				return (char)(value + 0x57);
			else
				throw new ArgumentOutOfRangeException("value", value, "valid range: 0 <= value <= 15");
		}
		/// <summary>
		/// Converts an array of <see cref="Byte"/> into an hexString.
		/// </summary>
		/// <param name="bytes">An array of <see cref="Byte"/> to convert.</param>
		/// <returns>An <see cref="String"/> representation of the given array of <see cref="Byte"/>.</returns>
		/// <exception cref="ArgumentNullException"><paramref name="bytes"/> is <see langword="null"/>.</exception>
		public static string ToString(byte[] bytes)
		{
			if (bytes == null)
				throw new ArgumentNullException("bytes");

			char[] chars = new char[bytes.Length << 1];
			for (int i = 0, bi = 0; i < chars.Length; )
			{
				byte b = bytes[bi++];
				chars[i++] = ToHexChar(b >> 4);
				chars[i++] = ToHexChar(b & 0xF);
			}
			return new string(chars);
		}
		/// <summary>
		/// Validates a <see cref="String"/> by removing all char which are not valid hex chars.
		/// </summary>
		/// <param name="hexString">A <see cref="String"/> to validate.</param>
		/// <returns>A validated <see cref="String"/>.</returns>
		/// <exception cref="ArgumentNullException"><paramref name="hexString"/> is <see langword="null"/>.</exception>
		public static string ValidateHexString(string hexString)
		{
			if (hexString == null)
				throw new ArgumentNullException("hexString");
			if (hexString.Length == 0)
				return hexString;

			int i = 0;
			for (; i < hexString.Length; i++)
			{
				if (hexString[i] != ' ')
					break;
			}
			if (i >= hexString.Length)
				return string.Empty;

			if (hexString.Length - i >= 2)
			{
				char one = hexString[i];
				char two = hexString[i + 1];
				if (one == '0' && (two == 'x' || two == 'X'))
					i += 2;
			}

			if (i >= hexString.Length)
				return string.Empty;

			var sb = new StringBuilder(hexString.Length - i);
			while (i < hexString.Length)
			{
				char c = hexString[i++];
				if (Hexadecimal.IsValidHexChar(c))
					sb.Append(c);
			}

			return sb.ToString();
		}
	}
}

EDIT: so was blödes, da waren noch ein paar käfer...
EDIT2: noch ne IsNullOrEmpty abfrage in IsValidHexString eingebaut
EDIT3: stark überarbeitet & kommentare eingefügt
EDIT4: weiter überarbeitet, leerzeichen am anfang werden nun übersprungen

Schlagwörter: <hexstring, hexadecimal, tostring, hexchar>

"In der Informatik geht es genauso wenig um Computer wie in der Astonomie um Teleskope."
Edsger W. Dijkstra

The Humble Programmer by Edsger W. Dijkstra

Gelöschter Account
vor 14 Jahren
                    (c >= 0x41 && c <= 0x46) || // is lower case
                    (c >= 0x41 && c <= 0x46))   // is upper case

das solltest du ändern. es is beides das selbe... entgegen dem kommentar.

TiltonJH Themenstarter:in
87 Beiträge seit 2006
vor 14 Jahren

Danke. Jup selber grad schon noch gersehn 😉

MfG TiltonJH

"In der Informatik geht es genauso wenig um Computer wie in der Astonomie um Teleskope."
Edsger W. Dijkstra

The Humble Programmer by Edsger W. Dijkstra

TiltonJH Themenstarter:in
87 Beiträge seit 2006
vor 14 Jahren

hab mal neueren code rein gestellt. 8)

-> stark überarbeitet & kommentare eingefügt

tilton

"In der Informatik geht es genauso wenig um Computer wie in der Astonomie um Teleskope."
Edsger W. Dijkstra

The Humble Programmer by Edsger W. Dijkstra

M
3 Beiträge seit 2009
vor 14 Jahren

Das mit der Überprüfung des Strings auf Gültigkeit geht mit RegEx sehr einfach



using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace McManta
{
    public static class HexStringConverter
    {         
        public static bool IsHexString(string text)
        {
       	    return Regex.IsMatch(text, @"^[0-9A-Fa-f]+$");
        }
     }
}


Dann noch unötiges entfernen bzw. nötiges hinzufügen oder ein paar Exceptions werfen falls nötig



public static byte[] String2Hex(string text)
{
           if (string.IsNullOrEmpty(text))
                throw new ArgumentNullException();
            
            // 0x entfernen
            if (text.StartsWith("0x"))
                text = text.Replace("0x","");
            
            // Leerzeichen entfernen z.B. 0a 0b 0c ff
            text = text.Replace(" ","");
            
            // 0 vorne hinzufügen, bei ungerader Anzahl von Zeichen
            if ((text.Length % 2) != 0)
                text = "0" + text;

            // Format prüfen
            if (!IsHexString(text))
                throw new FormatException();

            // konvertieren
            .........
       
          return byteResult;
           

 }


U
208 Beiträge seit 2008
vor 14 Jahren

Hallo McManta, hallo TiltonJH,

Ich würde bei der Validierung des HexStrings noch eine kleine Änderung vornehmen. Und zwar sollten die Leerzeichen im ersten Schritt entfernt werden und anschließend erst überprüft werden, ob der String mit "0x" beginnt. Ist zwar recht unwahrscheinlich, dass der String führende Leerstellen enthält, aber sicher ist sicher (eventuell könnte man es auch so erweitern, dass auch anderer Whitespace entfernt wird, bspw. Tabulatoren und Zeilenumbrüche).

Außerdem sollte die Überprüfung, ob der String mit "0x" anfängt, case insensitive realisiert werden:

if (text.Substring(0, 2).ToLower().StartsWith("0x"))
    text = text.Substring(2);

Grüße,

Isaac

TiltonJH Themenstarter:in
87 Beiträge seit 2006
vor 14 Jahren

Hallo,

Das mit der Überprüfung des Strings auf Gültigkeit geht mit RegEx sehr einfach

Das mag sein, aber das Parsing und das Ausführen der RegEx braucht ne ganze Ecke mehr Rechenschritte als meine Implementierung.
D.h. RegEx ist einfach zu implementieren bzw. zu nutzen (bringt das Framework schon mit), aber aus Sicht der Performance ist es aufwendiger.

  
...  
 text = text.Replace("0x","");  
...  
  

Ein Replace ist auch wieder aufwendiger als ein einfaches Überspringen der ersten 2 Zeichen. Ein Replace erzeugt einen neuen String (inkl. Speicherreservierung, Zeichen kopieren usw.). Von der Tasache das bei Replace der gesammte String mindestens einmal durchsucht werden muss, mal ganz abgesehn.

  
...  
if ((text.Length % 2) != 0)  
...  
  

Der Modulo wird intern mit Hilfe von Division errechnet (die genaue Formel hab ich grad nich im Kopf mal, wer 's wissen sollte mal Google/Wiki befragen 😉 ). Division ist eine sehr aufwendige Rechenoperation.
Ein simples Logisches & hingegen sehr sehr viel schneller.

  
if (text.Substring(0, 2).ToLower().StartsWith("0x"))  
    text = text.Substring(2);  
  

Alleine in "text.Substring(0, 2).ToLower()" werden 2 neue Strings erstellt.

Auf derlei Sachen wollte ich eben explizit verzichten.

Das Beachten/Filtern von Leerzeichen, ist jedoch eine Idee die ich mit einbauen könnte. Werde ich mir mal durch den Kopf gehen lassen.

Bei "StartsWith("0x")" kann ich mir vor stellen das es auch noch ein paar Optimierungen geben könnte, da werde ich mir mal anschaun, wie das dass Framework intern gelöst hat und ob was man besser machen könnte.

MfG

TiltonJH

"In der Informatik geht es genauso wenig um Computer wie in der Astonomie um Teleskope."
Edsger W. Dijkstra

The Humble Programmer by Edsger W. Dijkstra

1.130 Beiträge seit 2007
vor 14 Jahren

Mit meinem Zahlensystem Converter gehts auch, sofern man das richtige Alphabet angibt: new char[16]{'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};

Projekte:Jade, HttpSaver
Zum Rechtschreiben gibts doch schon die Politiker. Aber die bauen auch nur mist!

479 Beiträge seit 2008
vor 14 Jahren

Hallo,

schaut ja gut aus =) nur - diese Funktion besitzt das .NET Framework doch schon, mit der Convert Klasse? (Convert.ToString(0xAB, 16) und int.Parse("0x12", NumberStyles.HexNumber)).

mfg.
markus111

[Follow me on Twitter](http://twitter.com/blendingsky)
TiltonJH Themenstarter:in
87 Beiträge seit 2006
vor 14 Jahren

Hallo,

ja eine ähnliche Funktion besitzt das .Net Framework schon. Aber eben nur ähnlich.

Einen Int32 oder Int64 in hex wandeln und wieder zurück nützt ja nur für 1 bis 8 Byte lange HexStrings etwas. Und selbst dann ist das Ergebnis eben ein int und kein Byte[].

Es gibt keine Funktion mit der man von einen beliebig langen HexString in Byte[] wandeln kann. Auch gibt es keine mir bekannte Möglichkeit ein beliebig großes Byte[] in HexString wandeln könnte. Deswegen also hier diese Klasse.

MfG

TiltonJH

"In der Informatik geht es genauso wenig um Computer wie in der Astonomie um Teleskope."
Edsger W. Dijkstra

The Humble Programmer by Edsger W. Dijkstra

TiltonJH Themenstarter:in
87 Beiträge seit 2006
vor 14 Jahren

Hallo,

ich habe oben eine nochmal überarbeitete Version rein gestellt. Leerzeichen am Anfang werden nun übersprungen, auch die String.StartsWith() habe ich durch performanteren Code ersetzt.

Strings, wie beispielsweise: "0x01 23 45 67 89 ab cd ef AB CD EF", werden aber immer noch nicht übersetzt. Hier hilft ein:


Hexadecimal.ToBytes (Hexadecimal.ValidateHexString ("0x01  23  45  67  89  ab  cd  ef  AB  CD  EF"))

aber stark weiter 8).

MfG

Tilton

"In der Informatik geht es genauso wenig um Computer wie in der Astonomie um Teleskope."
Edsger W. Dijkstra

The Humble Programmer by Edsger W. Dijkstra