Laden...

string.Format() Problem

Erstellt von maik vor 16 Jahren Letzter Beitrag vor 16 Jahren 4.585 Views
M
maik Themenstarter:in
170 Beiträge seit 2005
vor 16 Jahren
string.Format() Problem

Hi zusammen,

ich habe da eine kleine Schwierigkeit mit string.Format()...
Und zwar lese ich über die serielle Schnittstelle durch einen Scanner eine bestimmte Anzahl bytes von einem Datenträger. Die bytes stellen für gewöhnlich Zeichen innerhalb der ASCII Tabelle dar. Sprich mit Encoding.ASCII.GetString(byte-array) bekomme ich den entsprechenden String zurückgeliefert.
Doch wenn der Scanner falsch konfiguriert wurde, kommt es häufig vor, dass er nur irgendwelchen Mist als Nutzbytes schickt. Der String der dann von der GetString() Methode geliefert wird enthält dann eben irgendeinen Stuss.

Egal in welchem Fall, sprich wenn Schrott oder korrekte Daten in dem ermittelten String drin stehen, muss eine Verarbeitung gestartet werden. Bei Schrott schlägt diese logischer Weise fehlt.

Wenn ich dann eine Fehlermeldung ausgeben will: "Die Daten '{0}' sind Schrott!" und dafür string.Format("Die Daten '{0}' sind Schrott", meineSchrottDaten); verwende kommt in der anzeigenden MessageBox folgendes an: "Die Daten meineSchrottDatenInhalt "

Der Rest wird einfach abgeschnitten...
Gibt es eine Möglichkeit den ankommenden String den ich in den Platzhalter einsetze ohne Escape-Sequenzen oder ähnliches einfach auszugeben?
Ich vermute, dass der String irgendwelche Daten enthält die string.Format() dazu zwingt den Rest abzuschneiden...

Wäre super, wenn mir jemand Hilfe geben könnte.
Thanks!

Gruß
Maik

B
214 Beiträge seit 2005
vor 16 Jahren

Ungetestet und keine Garantie für Funktionsfähigkeit 🙂

Aber wenn du ein @ Zeichen vor deinem stringnamen davor schreibst, müsste es gehen. Oder greift es hier nicht? (Frage an die anderen hier gerichtet 🙂)

string.Format("Die Daten '{0}' sind Schrott", @meineSchrottDaten);

Gruß Bionic

.:: SilvrGame - Browsergame Development with Silverlight
.:: Bionic's blOg

M
1.439 Beiträge seit 2005
vor 16 Jahren

Ich vermute auch, dass im String gewisse Steuerzeichen enthalten sind, die zu Problemen Führen, aber nicht unbedingt bei String.Format sondern beim Anzeigen.
Du könntest bei deinen 'Schrott'-Daten alle Zeichen durchgehen und nicht druckbare Zeichen durch einen Punkt ersetzen oder einen Hexstring anzeigen.

@Bionic: Das @-Zeichen ist hier fehl am Platz.

B
1.529 Beiträge seit 2006
vor 16 Jahren

@Bionic:

Oder greift es hier nicht?

Es greift nicht. Das @-Zeichen deaktiviert die Auswertung von Sonderzeichen in String-Literalen, d.h. direkt angegebenen String-Konstanten. Sind Sonderzeichen im String drin, sind sie drin und werden auch ausgewertet, es sei denn man entfernt sie. Einfach "ungültig" machen geht nicht.

@maik:
Ich vermute mal, dass dein String mit einem Nullzeichen ("\000") beginnt.
Ich vermute weiterhin, dass du die Daten als Byte-Array bekommst (bzw. bekommen solltest!?). Dann wandle sie vor der Ausgabe mittels BitConverter.ToString( byte[] ) in einen hexadezimalen String um.

49.485 Beiträge seit 2005
vor 16 Jahren
B
214 Beiträge seit 2005
vor 16 Jahren

Danke euch (herbivore und Borg) für die Erklärung 😉

.:: SilvrGame - Browsergame Development with Silverlight
.:: Bionic's blOg

M
maik Themenstarter:in
170 Beiträge seit 2005
vor 16 Jahren

Original von Borg
@maik:
Ich vermute mal, dass dein String mit einem Nullzeichen ("\000") beginnt.
Ich vermute weiterhin, dass du die Daten als Byte-Array bekommst (bzw. bekommen solltest!?). Dann wandle sie vor der Ausgabe mittels BitConverter.ToString( byte[] ) in einen hexadezimalen String um.

Ja korrekt, ist ein Byte-Array. Ah ok, wenn ich natürlich mittels dem BitConverter den korrekten String rausbekomme, wäre das von sehr großem Vorteil.
Was unterscheidet denn die beiden Methoden Encoding.ASCII.GetString(byte[]) von der BitConverter.ToString(byte[]), außer dem Encoding?

Würde damit das Problem mit z.B. "\000" behoben?

EDIT:

Mit dem BitConverter wird kein String ausgegeben mit dem Inhalt des byte[] sondern nur der einzelnen Elemente als String. Wie wenn ich mit einer for schleife auf jedem Element ToString() machen würde.
Sprich: Encoding.ASCII.GetString(byte[]) bringt mir z.B. "0123" und BitConverter.ToString() bringt dann 30-31-32-33... Wie kann ich das dann widerrum umwandeln?

Gruß & Danke
Maik

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo maik,

wie wäre es mit ausprobieren?


using System;
using System.Text;

static class App
{
   public static void Main (string [] astrArg)
   {
      Console.WriteLine (BitConverter.ToString (new byte [] { 0x41}));
      Console.WriteLine (Encoding.ASCII.GetString(new byte [] { 0x41}));
   }
}

Ausgabe:
41
A

herbivore

M
maik Themenstarter:in
170 Beiträge seit 2005
vor 16 Jahren

Ja ok, aber was fange ich damit an? Wenn ich weiterhin dann "\000" im String später stehen habe... Oder?

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo maik,

um null-Bytes aus dem String oder einen byte-Array zu löschen, musst du nichts umwandeln.

Bei Strings geht es mit String.Replace. Bei Byte-Arrays kopiert man das Byte-Array byteweise in ein neues und lässt dabei die Null-Bytes einfach aus.

herbivore

M
maik Themenstarter:in
170 Beiträge seit 2005
vor 16 Jahren

Ok, sprich sind Null-Bytes "\0"? Für String bedeutet das ja die Endsequenz, also das Ende des kompletten Strings. Ich möchte dem Benutzer dann ja nicht irgendwas rausschneiden sondern sagen dass die gescannten Daten ungültig sind.
Also wenn ich das byte[] durchgehe und "0" finde entspricht das "\0"?

Danke

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo maik,

wenn wir über einen 8bit Zeichensatz sprechen, ja, dann entspricht 0 '\0'.

herbivore

B
1.529 Beiträge seit 2006
vor 16 Jahren

Encoding.ASCII.GetString(byte[]) bringt mir z.B. "0123"

Richtig. Encoding betrachtet die einzelnen Bytes als einzelne Zeichen und erzeugt einen String, der genau aus diesen Zeichen besteht. Wenn du so willst, wandelt er dir jedes Byte einfach in ein Char.

und BitConverter.ToString() bringt dann 30-31-32-33...

Das war ja Sinn der Sache. Dein Problem war doch, dass die MessageBox Teile des Textes nicht darstellt, weil in dem String irgendwelche Steuerzeichen drin sind. Dieses Problem hast du damit gelöst, da jetzt die Werte der einzelnen Bytes hexadezimal ausgegeben werden.

Wie kann ich das dann widerrum umwandeln?

Wozu? Es ging doch nur darum, das erhaltene Byte-Array zur Ausgabe zu formatieren. Und da du offenbar prüfen willst, welche sinnlosen Daten dir der Scanner schickt, ist dir nicht geholfen, wenn die Hälfte der Bytes nicht angezeigt wird, bloß weil sie Steuerzeichen sind.

Das wichtigste: der Scanner wird dir doch kaum eine Zeichenkette schicken, sondern nur binäre Daten. Warum willst du die überhaupt in eine solche umwandeln? Da kann nur Blödsinn bei raus kommen.

S
8.746 Beiträge seit 2005
vor 16 Jahren

Achtung, Nullbytes sind zulässige Zeichen in einem .NET-String. Das ist bei Sprachen wie C/C++ anders, weil dort Strings null-terminiert sind. Beim Marshalling wird das beachtet. Bei den Converter- bzw. Encoding-Klassen nicht.

M
maik Themenstarter:in
170 Beiträge seit 2005
vor 16 Jahren

@Borg:
Ich muss die Daten in eine Zeichenkette umwandeln, da diese "Nummer" in dem Datenträger, welcher vom Scanner gelesen wird, anschließend in einer Datenbank abgefragt werden muss. Sprich, eigentlich müsste ich das gesamte byte[] jedes mal validieren indem ich durchgehe und alle möglichen Steuerzeichen erkenne und dann dem Benutzer ausgebe, das die angekommenen Daten falsch sind.

Eine Idee hierzu?

Hier mal mein spontaner Ansatz:


private bool IsValidByteArray(byte[] array)
{
  if (array != null && array.Length >= 1)
  {
    bool err = false;
    for (int i = 0; i < array.Length; i++)
    {
      if(array[i] == 0x00)
      {
        err = true;
        break;
      }
    }
    if (err)
      return false;
  }
  else
    return false;

  return true;
}

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo maik,

es geht auch etwas leichter:


private bool IsValidByteArray(byte[] array)
{
  if (array != null && array.Length >= 1)
  {
    for (int i = 0; i < array.Length; i++)
    {
      if(array[i] == 0x00)
      {
        return false;
      }
    }
  return true;
}

Mal abgesehen davon, dass


if (err)
      return false;
    }
    else
      return false;

statt

return !err;

ein Fall für [Tipp] Anfängerfehler == true / == false ist. 🙂

herbivore

B
1.529 Beiträge seit 2006
vor 16 Jahren

Ich habe es immer noch nicht richtig verstanden. Wieso schickt dir der Scanner plötzlich zwischendurch einfach mal so sinnlose Daten? Was ist dem voraus gegangen? Musst du den Scanner vielleicht neu initialisieren? Du hast doch geschrieben, dass die sinnlosen Daten kommen, wenn der Scanner falsch konfiguriert ist. Wieso konfiguriert ihn dein Programm nicht entsprechend?

Und was meinst du mit:

da diese "Nummer" in dem Datenträger, welcher vom Scanner gelesen wird,

Reden wir hier von einem optischen Scanner? Was für ein Datenträger wird dort gescannt?

M
maik Themenstarter:in
170 Beiträge seit 2005
vor 16 Jahren

ich hatte falsch eingerückt, deshalb wars wohl etwas schlecht leserlich... hab es korrigiert.

M
maik Themenstarter:in
170 Beiträge seit 2005
vor 16 Jahren

Original von Borg
Ich habe es immer noch nicht richtig verstanden. Wieso schickt dir der Scanner plötzlich zwischendurch einfach mal so sinnlose Daten? Was ist dem voraus gegangen? Musst du den Scanner vielleicht neu initialisieren? Du hast doch geschrieben, dass die sinnlosen Daten kommen, wenn der Scanner falsch konfiguriert ist. Wieso konfiguriert ihn dein Programm nicht entsprechend?

Und was meinst du mit:

da diese "Nummer" in dem Datenträger, welcher vom Scanner gelesen wird,

Reden wir hier von einem optischen Scanner? Was für ein Datenträger wird dort gescannt?

Ja optischer Scanner und nein ich initialisiere ihn nicht erneut, da die ganze Reihe von verschiedenen Typen sonst kodiert werden müssten. Ich warte nur auf die Daten am COM-Port und wandle diese dann nach String um. Die Müll-Daten die ankommen, betrifft nur den Kunden, weil er irgendwas falsch einstellt am Scanner. Ich habe es bis heute noch nicht rausgefunden, des weiteren scheint er mehrere Geräte an 1 COM-Port angeschlossen zu haben über ein COM-Switch... Sprich 3 Geräte an ein Switch welche auf 1 COM Port schicken...
Ich möchte nichts dergleichen überprüfen oder Konfigurieren. Möchte nur prüfen ob die angekommenen Daten korrekt sind, z.B. Steuerzeiche etc...

Gruß
Maik

M
maik Themenstarter:in
170 Beiträge seit 2005
vor 16 Jahren

P.S. Die Mülldaten kommen entweder immer oder garnicht. Also ist nicht ab und an so.

B
1.529 Beiträge seit 2006
vor 16 Jahren

Nun, dann musst du ja nur wissen, welche Zeichen erlaubt sind und welche nicht.

Ich würde dann in einem Bool-Array speichern, was erlaubte Zeichen sind und was nicht und das ganze so gestalten:

// für jedes Zeichen ein Element, true wenn erlaubt, false wenn verboten
bool[] AllowedBytes = new bool[256] { /* ... */ };

private bool IsValidData( byte[] Data, bool[] AllowedBytes )
{
   if ((AllowedBytes.Length != 256) || (Data == null))
      throw new ArgumentException();
   int i = 0;
   while ((i < Data.Length) && AllowedBytes[ Data[i] ])
     i++;
   return i == Data.Length;
}
M
maik Themenstarter:in
170 Beiträge seit 2005
vor 16 Jahren

Original von Borg
Nun, dann musst du ja nur wissen, welche Zeichen erlaubt sind und welche nicht.

Ich würde dann in einem Bool-Array speichern, was erlaubte Zeichen sind und was nicht und das ganze so gestalten:

// für jedes Zeichen ein Element, true wenn erlaubt, false wenn verboten  
bool[] AllowedBytes = new bool[256] { /* ... */ };  
  
private bool IsValidData( byte[] Data, bool[] AllowedBytes )  
{  
   if ((AllowedBytes.Length != 256) || (Data == null))  
      throw new ArgumentException();  
   int i = 0;  
   while ((i < Data.Length) && AllowedBytes[ Data[i] ])  
     i++;  
   return i == Data.Length;  
}  

ja ist korrekt, das wird das sicherste sein. da muss ich allerdings aufpassen dass die nutzdaten nie größer als 256 bytes sind. wäre es da nicht einfacher ein string array mit erlaubten zeichen zu machen und diese dann zu vergleichen?
sprich A-Z, 0-9 und a-z

ein bool array mit 256 werten is dann schon relativ unübersichtlich...
das wäre genau das array welches die ASCII zeichen A-Z, a-z und 0-9 als byte belegen:


private static bool[] _allowedBytes = new bool[256] { false, false, false, false, false, false, false,
                                                      false, false, false, false, false, false, false, 
                                                      false, false, false, false, false, false, false, 
                                                      false, false, false, false, false, false, false,
                                                      false, false, false, false, false, false, false,
                                                      false, false, false, false, false, false, false,
                                                      false, false, false, false, false, false, true, 
                                                      true, true, true, true, true, true, true, true,
                                                      true, false, false, false, false, false, false,
                                                      false, true, true, true, true, true, true, true, 
                                                      true, true, true, true, true, true, true, true, 
                                                      true, true, true, true, true, true, true, true, 
                                                      true, true, true, false, false, false, false, 
                                                      false, false, true, true, true, true, true, true,
                                                      true, true, true, true, true, true, true, true, 
                                                      true, true, true, true, true, true, true, true, 
                                                      true, true, true, true, false, false, false, false, 
                                                      false, false, false, false, false, false, false, 
                                                      false, false, false, false, false, false, false, 
                                                      false, false, false, false, false, false, false, 
                                                      false, false, false, false, false, false, false, 
                                                      false, false, false, false, false, false, false, 
                                                      false, false, false, false, false, false, false, 
                                                      false, false, false, false, false, false, false, 
                                                      false, false, false, false, false, false, false, 
                                                      false, false, false, false, false, false, false, 
                                                      false, false, false, false, false, false, false, 
                                                      false, false, false, false, false, false, false, 
                                                      false, false, false, false, false, false, false, 
                                                      false, false, false, false, false, false, false, 
                                                      false, false, false, false, false, false, false, 
                                                      false, false, false, false, false, false, false, 
                                                      false, false, false, false, false, false, false, 
                                                      false, false, false, false, false, false, false, 
                                                      false, false, false, false, false, false, false, 
                                                      false, false, false};

also da ist die variante vielleicht platzsparender und begrenzt das daten byte[] nicht auf die länge von 256.


private bool IsValidByteArray(byte[] array)
{
  string[] AllowedStringValues = new string[] { /* A,B,C,... */};
  for (int i = 0; j < array.Length; i++)
  {
    string val = Encoding.ASCII.GetString(array, i, 1);
    bool valid = false;
    for (int j = 0; i < AllowedStringValues.Length; j++)
    {
      if (AllowedStringValues[j].Equals(val))
        valid = true;
    }
    if(!valid)
      return false;
  }
  return true;
}

B
1.529 Beiträge seit 2006
vor 16 Jahren

da muss ich allerdings aufpassen dass die nutzdaten nie größer als 256 bytes sind

Wie kommst du denn darauf? Diese Einschränkung hat der Code nicht.

Aber du hast insofern Recht, als dass das Array sehr unübersichtlich ist.
Daher könntest du auch eine Liste verwenden, in der die erlaubten Zeichen eingetragen sind.
Allerdings muss dann bei jedem Zeichen (Byte) die Liste durchsucht werden, was die Laufzeit deutlich erhöht.

Und um ein derartiges Array von bool zu erstellen, kannst du dir ja eine eigene kleine Hilfsmethode schreiben. Es hat ja niemand verlangt, dass du das von Hand machst.

M
maik Themenstarter:in
170 Beiträge seit 2005
vor 16 Jahren

ah korrekt sorry. hatte mich verlesen... dachte es wäre Data.Length != 256 gewesen. ja mit der hilfsmethode hätte ich mir auch das array basteln können richtig. aber rein von der leserlichkeit eines zweiten ist es einfacher ein array mit erlaubten zeichen zu betrachten als ein bool array oder eine methode die ein solches baut.

insofern hast du wiederrum recht, dass es ja etwas länger dauert da jedes byte im nutzdaten array erst decodiert und anschließend mit jedem element des erlaubten zeichen arrays abgeglichen werden muss.