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 <= value <= 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
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
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
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;
}
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
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
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'};
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
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
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