Fragensteller
txt-Datei lesen und als ByteArray im Hexformat bearbeiten...

Frage
-
Hallo,
ich habe eine Datei (im hex-format - den Inhalt eines EEProms eines µCs). Diesen möchte ich auslesen und verändern.
Da ich aus der µC-Programmierung komme, bin ich (noch) etwas unbeholfen in C# :-)
Hier mal eine Zeile dieses Memoryinhaltes:
:10E000000000D00007000000000000000100000038
diese bekomme ich schon eingelesen (mit StreamReader) und meine "Nutzdaten" - hab ich schon herausgesplittet (als String aber nur)
So habe ich diesen String zur Verfügung, den ich weiterverarbeiten will:
"0000D00007000000000000000100000038"
Das Ziel ist es nun, immer 2 Character als ein Byte zu "interpretieren".
Als nächstes jedes 2.Byte entfernen (das sind immer 0x00, die vom EEProm her eingefügt werden und uninteressant sind).
D.h. ich hätte gerne ein ByteArray - im Bsp dann mit folgendem Inhalt:
Memory = { 0x00, 0xD0, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x38 }
Wie macht man das?
was ich bisher bemerkt habe: 00 (aus dem String) wird in mein ByteArray eingelesen als 0x30 0x30 (ASCII?) und nicht zusammengefasst...
ich bin am Verzweifeln :-(
Über jegliche Tipps, bin ich euch dankbar.
Gruß
Stefan
hier noch ein paar Codezeilen...
MemoryData beinhaltet mein Byte-Array. Es werden hier 5 Zeilen eingelesen...
public Boolean loadDataFromFile( Stream FileName )
{
using (StreamReader reader = new StreamReader(FileName))
{
string []tempLines = null;
tempLines = reader.ReadToEnd().Split(new char[] { '\n' });
RawData MemoryData = new RawData();
//line after line
int k = 0;
for (int i = 1; i < 5; i++)
{
String Insert;
Insert = tempLines[i].Substring(9,(tempLines[1].Length-10));
for (int j = 0; j < Insert.Length-1; j++)
{
MemoryData.Data[k++] = Convert.ToByte(Insert[j];
}
}
MemoryData.DataSize = 10;//nur zum Debuggen, dass ich MemoryData anschauen kann :-)
}
return true;
}
- Bearbeitet Stefan0815 Dienstag, 30. November 2010 13:57 code eingefügt
Alle Antworten
-
Hi,
ich hatte in einem meiner ersten Projekte (was nich lange her ist), auch mit der Wandlung von Hex zu String und umgekehrt zu tun. So wie ich das sehe sortierst du in deinem String aus und willst das ganze nun in Hex wandeln. Korrigiere mich wenn ich das falsch sehe.
Ich hatte da Methoden für geschrieben. Nicht perfekt aber zum Schluss haben sie funktioniert. Hex geht als String rein und immer 2 Zeichen kommen in ein Byte. Quasi was du suchst. Kannst es ja mal testen und sagen ob es klappt.
private byte[] HexToByte(string msg)
{
byte[] comBuffer = new byte[msg.Length / 2];
for (int i = 0; i < msg.Length; i += 2)
comBuffer[i / 2] = (byte)Convert.ToByte(msg.Substring(i, 2), 16);
return comBuffer;
}mfg spezieh
P.S. Bitte gewöhn dir möglichst früh einen einheitlichen Stil an. Das hilft auch beim lernen und man stößt bei der suche noch auf andere nützliche Tips. ich zumindest. Noch ein paar Beispiele warum ich das sage: Mal deklarierst du einen String mit "string" und kurz darauf mit "String", auch nicht grade gut lesbar und ich bin erstaunt, dass das funktioniert "string []tempLines". Dazu kommt noch mal deklarierst du Variablennamen in der Methode groß und mal klein. Stört den Compiler nicht aber den Lesefluss.
Schau dir z.B. das mal an. http://msdn.microsoft.com/en-us/library/x2dbyw72%28v=vs.71%29.aspx
-
Hallo Stefan,
zum Konvertieren von Hex <=> Byte-Array siehe:
How do you convert Byte Array to Hexadecimal String, and vice versa, in C#?(die erste sollte es für den Anfang tun).
Zudem solltest Du beachten, dass ein StreamReader als Standard UTF-8 als Kodierung verwendet.
Das fällt erst auf, wenn Du Zeichen über ASCII (> 128) hast. sicherer wäre Encoding.Default
für Windows ANSI (oder ASCII) zu übergeben, wenn die Datei damit erstellt wurde
(bei älteren Programmen die Regel).Zu 00 (aus dem String) wird in mein ByteArray eingelesen als 0x30 0x30 (ASCII?) und nicht zusammengefasst...
Das ist bereits die hexadezimale Darstellung einer "0", denn 0x30 - 0x39 entspräche den Ziffern "0" bis "9"
siehe ZeichentabelleGruß Elmar
-
Hallo zusammen,
sorry Spezieh, dass es deine Methode trifft. Aber ich muss an diesem Beispiel einfach mal wieder für die Lambdas werbetrommeln:
return msg.Where(t => (msg.IndexOf(t)+1) % 2 == 0).Select(t => Convert.ToByte(t.ToString(),16)).ToArray();
Mithilfe von Lambdas kannst du aus dem 4 Zeiler, einen schönen, deklarativen 1 Zeiler machen.
Viele Grüße
Holger M. Rößler
-
Hallo Holger,
Deine Methode hat einen Schönheitsfehler, denn sie liefert falsche Ergebnisse ;-)
Was die Werbetrommel angeht und wir hier in einem Anfängerthread sind,
ist meine Meinung ist eher, jede Aufgabe sollte einmal auf konventionelle
Methode gelöst und der Algorithmus verstanden worden sein...
Danach darf man ihn in Assembler oder auch in LINQ schreiben ;-)Zum Abschluß mal eine LINQ Version meinerseits:
Was ich aber für größere Mengen nicht einsetzen würde, da mir die vielen Substringspublic static byte[] HexToByteLinq(string value) { return (from byteIndex in Enumerable.Range(0, value.Length / 2) select (byte)Convert.ToByte(value.Substring(byteIndex * 2, 2), 16)) .ToArray(); }
heftig aufstossen und auch Convert.ToByte ist nicht unbedingt ein Renner -
könnte daran liegen, dass ich vor langer Zeit mal Assembler programmiert hatte ;-)Wie aber auch im anderen Posting gesagt: Für den Anfang reichts -
Speziehs Varainte entspricht der ersten in meinem StackOverflow Link.Gruß Elmar
-
Hallo Holger,
keine Ursache. Zerleg meine Sachen nur. Hilft mir auch beim lernen. Mit Lambdas hab ich mich momentan noch nicht beschäftigt. Grade mal was davon gehört und um ehrlich zu sein versteh ich deshhalb auch kaum was in dem Einzeiler passiert.
Muss mir das wohl mal anschaun, demnächst.
mfg spezieh
-
Wow - bin überwältigt von den vielen Antworten! - DANKE EUCH!
Spezieh, vielen Dank für die Funktion
private byte[] HexToByte(string msg) { byte[] comBuffer = new byte[msg.Length / 2]; for (int i = 0; i < msg.Length; i += 2) comBuffer[i / 2] = (byte)Convert.ToByte(msg.Substring(i, 2), 16); return comBuffer; }
- sie funktioniert bestens! ;-)
dein Tipp - gleich von Beginn an bissel auf "Code-Gestaltung" zu achten nehm ich mir zu Herzen - aber wie es sicherlich vielen von euch geht - erstmal gucken, dass das Dingens läuft, dann auf das andere schaun - auch im Bezug auf Kommentierung/Dokumentation usw. :-) Und beim Ändern von Namen usw. ist hier VS natürlich einsame spitze *ggg*
...jetzt muss ich mal schaun, ob ich das mit "jedes 2. Byte verwerfen" hinbekomme.
werde erstmal diese Funktion verwenden - ist jedenfalls verständlicher als die von Holger (hat eher was mit C-zu tun :-)
- aber auch Danke für die Kurzform!
Ihr hört von mir!!!
Gruß
Stefan
-
Hallo Elmar,
ich habe diese Linqquery bei mir im VS getestet. Bei mir lieferten in mehreren Testdurchläufen beide Methoden das exakt gleiche Ergebnis! Mit welchem Parameter liefert meine Implementierung denn ein falsches Ergebnis? *staun*
Viele Grüße
Holger M. Rößler
-
Hallo Holger,
Du "vergisst" das obere Nibbel.
Die drei Funktionen in einen "Unit-Test für Arme" verpackt:
Gruß Elmarusing System; using System.Linq; using System.Text; namespace ElmarBoye.Samples.Code { class ConvertBytes { internal static void TestAll() { Test("Spezieh", HexToByteSpezieh); Test("Holger", HexToByteHolger); Test("Elmar", HexToByteElmar); } internal static void Test(string variante, Func<string, byte[]> method) { var values = new string[] { "0123456789ABCDEF", "0000D00007000000000000000100000038" }; foreach(var value in values) { var result = method(value); var testValue = ByteArrayToString(result); System.Diagnostics.Debug.Assert( // Groß-/Klein nicht relevant value.Equals(testValue, StringComparison.OrdinalIgnoreCase), String.Format("Abweichendes Ergebnis bei {0}: '{1}' => '{2}'", variante, value, testValue)); } } private static byte[] HexToByteSpezieh(string msg) { byte[] comBuffer = new byte[msg.Length / 2]; for (int i = 0; i < msg.Length; i += 2) comBuffer[i / 2] = (byte)Convert.ToByte(msg.Substring(i, 2), 16); return comBuffer; } private static byte[] HexToByteHolger(string msg) { return msg.Where(t => (msg.IndexOf(t) + 1) % 2 == 0).Select(t => Convert.ToByte(t.ToString(), 16)).ToArray(); } public static byte[] HexToByteElmar(string value) { return (from byteIndex in Enumerable.Range(0, value.Length / 2) select (byte)Convert.ToByte(value.Substring(byteIndex * 2, 2), 16)) .ToArray(); } // http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa-in-c public static string ByteArrayToString(byte[] ba) { StringBuilder hex = new StringBuilder(ba.Length * 2); foreach (byte b in ba) hex.AppendFormat("{0:x2}", b); return hex.ToString(); } } }
-
Hallo Stefan,
hier eine Variante mit "Wegwerfen" des zweiten (Nul-)Bytes:
internal static void TestHexByteFromShort() { var values = new string[] { "", "A5", "1234", "0123456789ABCDEF", "0000D00007000000000000000100000038" }; foreach (var value in values) { var result = HexToByteFromShort(value); var testValue = ByteArrayToString(result); Console.WriteLine("Ergebnis '{0}' => '{1}'", value, testValue); } } public static byte[] HexToByteFromShort(string value) { // Ausrichten auf 4 Zeichen if ((value.Length % 4) != 0) value = value.PadRight(value.Length + value.Length % 4, '0'); byte[] buffer = new byte[value.Length / 4]; for (int charIndex = 0; charIndex < value.Length; charIndex += 4) buffer[charIndex / 4] = (byte)Convert.ToByte(value.Substring(charIndex, 2), 16); return buffer; } // aus http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa-in-c public static string ByteArrayToString(byte[] ba) { StringBuilder hex = new StringBuilder(ba.Length * 2); foreach (byte b in ba) hex.AppendFormat("{0:x2}", b); return hex.ToString(); }
und zeigt nebenbei wie man sich eine kleine Testfunktion schreibt, um Randbedingungen zu prüfen.
Gruß Elmar
-
Danke Elmar,
das schau ich mir mal genauer an!
Ich hab's jetzt mal so gemacht:
schon vor der Stelle, an der ich den ganzen String in das Byte-Array konvertiere...
//take the data for use // Line 1..4, from char 9 till char 42, // then delete every 2nd byte (delete the needless 0x00's) for (int i = 0; i < 4; i++) { strArrUsedData[i] = strArrDataLinewise[i+1].Substring(9, 34); //delete every 2nd "byte" string newString = ""; for (int j = 0; j < 36; j++) { newString += strArrUsedData[i].Substring(j, 2); j += 3; } strArrUsedData[i] = newString; }
-
-
Hallo Stefan,
kann man auch machen - es gibt dabei viele Weg, die nach Rom führen ;-)
Und bei so einem Übungsprojekt, sollte man auch unterschiedliche Wege probieren,
um die angenehmsten zu finden.Wobei man einige Dinge berücksichtigen sollte,
will man den Code robust und effizient gestalten:Unter .NET sind Zeichenketten (System.String) unveränderlich,
d.h. jede Änderung erzeugt eine neue Zeichenkette.
Und führt man die Änderungen in zwei (oder mehr) Stufen durch, vervielfacht sich das.
Deswegen soll man bei intensiven Änderungen den StringBuilder einsetzen,
der den Verbrauch optimiert, siehe Verwenden der StringBuilder-KlasseUm Code robust zu gestalten, vermeidet man magische Zahlen direkt einzucodieren.
So wäre oben die 34 vs. 36??? eine solche,
ebenso die Zahl der Zeilen (4), die der Datei entnommen werden.
Dafür definiert man i. a. Konstanten (siehe const ), z. B.:So hat man weniger Aufwand, wenn sich dort etwas ändertconst int NumberOfLines = 4; const int CharacterPerLine = 36; const int LineOffset = 9;
und erkennt sofort woher die Zahl stammt.Und wo es geht bezieht man sich auf bereits vorhande Information,
um die Stellen zu minimieren, bei denen man was ändern muß,
sollte sich daran etwas ändern.Insofern war das ursprüngliche:
tempLines[i].Substring(9,(tempLines[1].Length-10));
günstiger als ein
strArrDataLinewise[i+1].Substring(9, 34);
(nur die 9 sollte man als Konstante einführen). Und anstatt
for (int j = 0; j < 36; j++)
auf die Länge, die bereits bekannt ist, Bezug nehmen:
(und das zweimalige Erhöhen von j darf man auch zusammenfassen)for (int j = 0; j < strArrUsedData[i].Length; j += 4) newString += strArrUsedData[i].Substring(j, 2);
Gruß Elmar
-
Hallo Stefan,
der Code in der Form "wirft" die Ergebnisse weg.
Setze Dir an die Stellen, wo Du gucken willst einen Breakpoint (Tastatur: F9).
Auch kannst Du in einzelnen Schritten durch den Code laufen, siehe Debug Menü.Gruß Elmar
- Bearbeitet Elmar Boye Mittwoch, 1. Dezember 2010 14:09 Korrektur
-
ja - das ist schon c-ähnlicher :-)
wie gesagt, war ja erstmal nur, um die Funktion zu überprüfen...
in C würde ich dafür #defines verwenden... gibt es sowas in der Art auch in C#? oder
wären das die von dir erwähnten Konstanten?
Meine andere Frage bezügl. "Konsole anhalten" hat sich erübrigt... mit Console.Read() :-)
Merci
C# hat zwar schon Gemeinsamkeiten mit dem mir bekannten C (ANSI-C), aber ist doch grundlegend anders *ggg*
-
Hallo Stefan,
im Moment überschneiden sich unseren Antworten...
Zum #define siehe meine vorherige Antwort bezüglich const.
Wobei das const in C# sehr limitiert ist im Vergleich zu #define,
nur braucht man das in objektorientierten Sprachen so nicht mehr.Gruß Elmar
-
Hallo Elmar,
eieiei, tatschächlich verdaddelt sich meinen Methode. *Boah, geht mal gar nicht* :o(
Komischerweise hat mir die Methode bei 3 Tests mit 3 zufällig gewählten Werten 3 mal das gleiche Ergebnis rausgebracht wie bei der Schleife!
Hier fühle ich mich irgendwie von der Mathematik wieder komplett verseppelt! *lach*
Danke für den Hinweis!
Viele Grüße
Holger M. Rößler
- Bearbeitet Holger M. Rößler Mittwoch, 1. Dezember 2010 19:38 Rechtschreibung
-
Hey,
hab noch ne Frage...
gibt es eine Funktion, die alle Text- und Comboboxen zusammenfasst, wenn ich auf Änderungen reagieren möchte?
D.h. die xxx.SelectedIndexChanged bzw. yyy.TextChanged in eine Art "esHatSichIrgendwasGeaendert" ;-)
Danke + Gruß
Stefan
-
Hallo Stefan,
zunächst kannst Du das bereits im Windows Forms Designer tun.
Markiere die Steuerelemente, denen Du einen gemeinsamen EventHandler verpassen willst.
Wähle das Ereignis im Eigenschaftsfenster aus.
Dort schlägt Dir zum einen Visual Studio bereits bestehende vor,
zum anderen kannst Du einen neuen Namen eingeben.Via Code geht es, in dem man die Controls-Auflistung durchläuft. Etwas Luftcode:
private void WireControlEvents() { foreach (Control control in this.Controls) { if (control is TextBox) control.TextChanged += CommonEventHandler; else if (control is ComboBox) ((ComboBox)control).SelectedIndexChanged += CommonEventHandler; } } private void CommonEventHandler(object sender, EventArgs e) { var control = sender as Control; Console.WriteLine("Event: {0}", control.Name); }
In den weitaus meisten Fällen würde man jedoch die Datenbindung nutzen.
Das ist jetzt aber weit von der Ausgangsfrage weg.
Ist das damit nicht geklärt, Du mache bitte dafür einen neuen Thread auf.Gruß Elmar