Laden...

Kulturunabhängige Konvertierung von bytes/chars nach ANSI-string und zurück

Erstellt von Votug vor 11 Jahren Letzter Beitrag vor 11 Jahren 2.334 Views
V
Votug Themenstarter:in
14 Beiträge seit 2009
vor 11 Jahren
Kulturunabhängige Konvertierung von bytes/chars nach ANSI-string und zurück

Hallo Community,

meine SW bekommt von einem C-Programm diverse Zeichenfolgen/strings als Bytes angeliefert.

Mit der folgenden Funktion habe ich bisher die Zeichen nach ANSI-string konvertiert:


public static string ByteToString(byte __b)
{
      if (__b == '\0')
              return "";
      return Encoding.Default.GetString(new byte[] { __b });
}

Das Problem daran ist (Encoding.Default), dass ich auf anderen kulturellen Systemen (z.B. Indisches OS) nicht das gewünschte Ergebnis erziele, z.B.:


string s1 = "";
string s2 = ByteToString(0x00);
string.Compare(s1, s2); // => Liefert Wert ungleich 0 obwohl strings identisch sein sollen!

Wie konvertiere ich korrekt und kulturunabhängig byte-Zeichenfolgen nach ANSI-Strings?

Meine Idee:


public static string ByteToString(byte __b)
{
      if (__b == 0x00)   // sollte mein Beispielproblem lösen
              return "";
      return Encoding.Default.GetString(new byte[] { __b });  // hier müsste ich dann aber immer noch Probleme nach der Konvertierung haben?. Wie macht mans richtig?
}

Und wie vergleiche ich zwei strings korrekt, da ja "string.Compare" laut MSDN auch kulturabhängig vergleicht?!
Würde der direkte Vergleich mit:

if(s1==s2)
    ; // Do something

dieses Problem lösen?

Ich bin für jeden Hinweis dankbar...

Danke & Gruss
Votug

225 Beiträge seit 2005
vor 11 Jahren

das sollte Dir weiter helfen wie man das Problem auch lösen könnte:

Verwenden der CurrentCulture-Eigenschaft

oder hier

CultureInfo.CurrentCulture-Eigenschaft

49.485 Beiträge seit 2005
vor 11 Jahren

Hallo Votug,

in deinem ersten Beispiel sollte das Encoding überhaupt noch nicht zum Tragen kommen. Ich habe ein Testprogramm sowohl mit __b == '\0' als auch __b == 0x00 übersetzt. Es kommt in beiden Fällen identischer IL-Code heraus. Das bedeutet, dass es für den Vergleich keinen Unterschied macht, ob das Literal eine Zahl oder ein Zeichen ist. Beide Literale werden als (Integer-)Wert 0 interpretiert. Insofern müssen im ersten Beispiel s1 und s2 beide der leere String sein. Und da sollte jeder Vergleich (ob nun, Compare oder Equals oder ==, ob nun kulturabhängig oder nicht) 0 bzw. true liefern, also die beiden als gleich erkennen.

Wenn an diesem Punkt deine Vermutung nicht stimmt oder die beobachtete Situation nicht mit der Realität übereinstimmt, sind möglicherweise auch andere von dir getroffene Annahmen falsch. Du solltest dich also im eigenen Interesse vergewissern, dass du nicht auch an anderer Stelle von falschen Annahmen ausgehst. Das ist nicht böse gemeint, sondern oft die Lösung von Problemen, wo sich die Dinge nicht so verhalten, wie man denkt, dass die sein sollten. Es kann dann zwar sein, dass man wirklich noch einen Fehler im Programm hat, es kann aber eben auch sein, dass die Annahmen oder Beobachtungen falsch sind.

Für dein eigentliches Encoding-Problem ist die Frage, wo die Strings erzeugt werden. Wenn alle Strings auf demselben System mit ByteToString erstellt werden, sollte es (fast) egal sein, welches Encoding du verwendest und es sollte auch egal sein, ob du kulturabhängig vergleichst oder nicht. Anders sieht das nur aus, wenn die Strings aus unterschiedlichen Quellen stammen, also z.B. einer direkt mit ByteToString erstellt und ein anderer aus einer Datei, vorher die mit ByteToString auf einem anderen Rechner mit anderen Einstellungen erstellt wurde.

Da du jedoch sagst, dass du die Zeichenfolgen sowieso als Bytes geliefert bekommst, kannst du dir den ganzen Zauber sparen und einfach und gleichzeitig sicherer auf Byte-Ebene vergleichen.

herbivore

V
Votug Themenstarter:in
14 Beiträge seit 2009
vor 11 Jahren

Hallo ihr beiden!

@ husky410: Danke an für den Hinweis! Werde beides durchlesen...

@herbivore: Auch dir vielen Dank. Bin dir nicht böse, Ratschläge sind immer gerne gesehen!

... aber:
Es ist definitiv so, dass auf einem nicht-europäischen System folgender Vergleich nicht identisch ist (hier in Europa läuft die SW bereits seit mehreren Monaten erfolgreich und ohne solche Probleme):


public static string ByteToString(byte __b)
 {
       if (__b == '\0')
               return "";
       return Encoding.Default.GetString(new byte[] { __b });
 }

string s1 = "";
string s2 = ByteToString(0x00);
string.Compare(s1, s2); // => Liefert Wert ungleich 0 obwohl strings identisch sein sollen!

Ich behaupte jetzt mal dass es keine anderen Nebeneinwirkungen im Code gibt. Daten werden auf Byte-Ebene empfangen sofort mittels obiger Methode umgewandelt und mit einer Benutzereingabe verglichen (=Passwortabfrage). Falls also kein Passwort gesetzt ist (Empfang von mehrern '0x00' und anschließende Konvertierung, geht der Vergleich in die Hose.

Ich habe jetzt ein bisschen mit diversen Culture-Infos herumgespielt und ich komme auch zu dem Ergebniss dass ein Vergleich der einzelnen Bytes zu einem korrekten Ergebnis führt, unabhängig von der Systemkultur.

Gruss
Votug

49.485 Beiträge seit 2005
vor 11 Jahren

Hallo Votug,

dass auf einem nicht-europäischen System folgender Vergleich nicht identisch ist

wo wurden die Quellen übersetzt? Alle auf einem europäsichen System oder Teile hier und Teile in Indien. Wenn alles hier übersetzt ist, sollte meine Aussage über die IL-Code zutreffen. Dann sehe ich aber nicht, warum ein Integer-Vergleich (der ja aus dem Quellcode wird) von 0 und 0 auf unterschiedlichen Systemen unterschiedliche Ergebnisse liefern sollte.

Aber auch hier sind wir wieder beim Thema möglicherweise falsche Annahmen. Denn es gibt ja mindestens zwei potenzielle Stellen, die das Ergebnis beeinflussen. Zum einen der Vergleich auf '\0' bzw. 0x00 (was wie gesagt keinen Unterschied machen sollte) und der Vergleich zweier leerer Strings (was allerdings auch keinen Unterschied machen sollte, aber wer weiß, ob es da nicht doch irgendwelche Kulturunterschiede gibt, wenn das eine Literal in einem Code steht, der hier und das andere Literal in einem Code steht, der in Indien übersetzt wurde). Wie dem auch sei, wäre es wichtig, die Ursache auf eine konkrete Stelle einzugrenzen ...

ich komme auch zu dem Ergebniss dass ein Vergleich der einzelnen Bytes zu einem korrekten Ergebnis führt, unabhängig von der Systemkultur.

... es sei denn, dein Problem ist damit sowieso schon gelöst.

herbivore

V
Votug Themenstarter:in
14 Beiträge seit 2009
vor 11 Jahren

Hallo Herbivore,

beide Systeme sind von mir, also auch 'hier' übersetzt.
Als Default-Passwort ist im Zielsystem '\0''\0''\0''\0''\0' eingetragen, also ein leerer String.

Greife ich auf das Ziel-System zu, läuft alles wie es soll.
Greift ein indischer Kunde mit meiner bei 'uns' funktionierenden .NET-SW auf das gleiche System kommt er nicht über die Passwortabfrage hinaus da der Vergleich schief geht.

Es ist also sichergestellt dass ich und der Kunde das gleiche Empfangen, da beide SW-Systeme identisch, bis auf die Plattform-Kultur.

Votug

C
258 Beiträge seit 2011
vor 11 Jahren

Aso "" ist laut compair auch nicht das selbe wie "\0"
ich hab mal bei mir auf dem versucht mit ein paar verschiedenenen Culturen das Problem zu finden jedoch konnte ich das ganze nicht rekonstruieren.

Dennoch habe ich einen Vorschlag versuchs doch erstmal damit:

        static void Main(string[] args)
        {
            Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("ar-AE");

            string test1 = "";
            string test2 = ByteToString(0x00);

            Console.WriteLine(string.Compare(test1.Replace("\0", ""), test2.Replace("\0", "")).ToString());
            Console.ReadLine();
        }

        public static string ByteToString(byte __b)
        {
            return Encoding.Default.GetString(new byte[] { __b });
}
49.485 Beiträge seit 2005
vor 11 Jahren

Hallo Votug,

da der Vergleich schief geht.

ist wirklich verbindlich geklärt und überprüft, dass es tatsächlich an dem Vergleich liegt? Möglicherweise liegt der Fehler doch noch irgendwo anders.

Ansonsten kann ich nur wiederholen, wie ich die Lage einschätze. Wenn alles an einer Stelle übersetzt wurde, dann muss IL-Code eigentlich so sein, wie ich es beschrieben habe, und der resultierende Integer-Vergleich ist nicht kulturabhängig. Auch die Stringliterale für den leeren String sollten dann identisch sein, so dass auch deren Vergleich erfolgreich sein müsste.

herbivore

Hallo Console32,

Aso "" ist laut compair auch nicht das selbe wie "\0"

nein, natürlich nicht. In C# sind Strings nicht '\0'-terminiert, wie in C, sondern können auch innerhalb des String (beliebig oft und an beliebigen Stellen) '\0' enthalten.

Allerdings wird der Fall '\0' von der ByteToString-Methode von Votug ja auch schon abgefangen und liefert dann "" und erst dieser Wert wird gegen "" verglichen.

herbivore

V
Votug Themenstarter:in
14 Beiträge seit 2009
vor 11 Jahren

Hallo herbovire,

ja, das Problem ist definitiv der von mir gezeigte string-Vergleich!

Ich war mir auch sicher, dass der Vergleich auf int-Ebene stattfindet, so wie du es beschrieben hast.

Hallo Console32,

dein Programm liefert zwar das gewünschte Ergebnis, aber ein "\0" habe/bekomme ich gar nicht durch meine Konvertierung von Byte '\0' nach string!

Gruss
Votug

C
258 Beiträge seit 2011
vor 11 Jahren

Also das Encoding liefert bei mir wenn ich 0 übergebe "\0" zurück.

@herbivore:
ich wusste das die strings nicht null terminiert sind jedoch wusste ich nicht das ein string vollgefüllt mit "nichts" nicht das selbe für vergleichsoperatoren ist wie "".

ich hab jetzt mit ildasm die exe geöfnet und :

b == 0
b == 0x00
b == '\0'

resultieren im exakt selben IL code (zumindest bei mir) kannst du dir ansehen was bei dir da rauskommt?

sollte unter *SDK -> Tools zu finden sein.

V
Votug Themenstarter:in
14 Beiträge seit 2009
vor 11 Jahren

Hallo herbivore,

ich bin mit deinen Aussagen vollkommen d'acord ...

Machen wir lieber mal einen break.

Ich werde mir bei nächstmöglicher Gelegenheit erstmal einen TeamViewer-Kontakt herstellen,
weil langsam aber sicher beschleicht mich das Gefühl dass der Kunde nicht meine neue SW nutzt
sondern evtl. eine alte in C++ geschriebene, und daher der Fehler rühren könnte. (Installation in unterschiedliche Verzeichnisse und falscher Programmaufruf oder so, würde mich nicht wundern... manchmal erlebt man die tollsten Dinge.)

Sobald ich mehr herausgefunden habe, melde ich mich wieder.

Danke & Gruss
Votug