Konvertieren eines Strings in ein Byte-Array in C #


668

Ich konvertiere etwas von VB in C #. Ein Problem mit der Syntax dieser Anweisung:

if ((searchResult.Properties["user"].Count > 0))
{
    profile.User = System.Text.Encoding.UTF8.GetString(searchResult.Properties["user"][0]);
}

Ich sehe dann folgende Fehler:

Argument 1: Konvertierung von 'Objekt' in 'Byte []' nicht möglich

Die beste überladene Methodenübereinstimmung für 'System.Text.Encoding.GetString (byte [])' enthält einige ungültige Argumente

Ich habe versucht, den Code basierend auf diesem Beitrag zu korrigieren , aber immer noch keinen Erfolg

string User = Encoding.UTF8.GetString("user", 0);

Irgendwelche Vorschläge?


1
Was ist die Art von searchResult.Properties["user"][0]? Versuchen Sie es byte[]zuerst zu
werfen

mshsayem ging wohin ich ging. Vermissen Sie eine Besetzung für a (byte[])im searchResult?
Harrison

2
Sie müssen herausfinden, welcher Typ Properties["user"][0]ist. Wenn Sie sicher sind , ist es ein Byte - Array , dann können Sie wie diese werfenprofile.User = System.Text.Encoding.UTF8.GetString((byte[])searchResult.Properties["user"][0]);
keyboardP

1
Es stellte sich heraus, dass all diese Aufregung nicht nötig war. Der Benutzername konnte doch ohne Codierung abgerufen werden.
Nouptime

3
Warum wählst du keine wahre Antwort aus?
Ali

Antworten:


1182

Wenn Sie bereits ein Byte-Array haben, müssen Sie wissen, welche Art von Codierung verwendet wurde, um es in dieses Byte-Array zu verwandeln.

Wenn das Byte-Array beispielsweise folgendermaßen erstellt wurde:

byte[] bytes = Encoding.ASCII.GetBytes(someString);

Sie müssen es wieder in eine Zeichenfolge wie die folgende umwandeln:

string someString = Encoding.ASCII.GetString(bytes);

Wenn Sie in dem von Ihnen geerbten Code die Codierung finden, die zum Erstellen des Byte-Arrays verwendet wurde, sollten Sie festgelegt werden.


3
Timothy, ich habe den VB-Code durchgesehen und kann anscheinend kein Byte-Array finden, wie Sie erwähnt haben.
Nouptime

Welchen Typ hat die Eigenschaft Properties in Ihrem Suchergebnis?
Timothy Randall

Ich kann nur sehen, dass Eigenschaften eine Reihe von Elementen als Zeichenfolge angehängt sind. Ich bin mir nicht sicher, ob du mich das gefragt hast.
Nouptime

16
@AndiAR versuchen Encoding.UTF8.GetBytes (somestring)
OzBob

1
Für meine Situation fand ich, dass Encoding.Unicode.GetBytes funktionierte (aber ASCII nicht)
Jeff

106

Fügen Sie zunächst den System.TextNamespace hinzu

using System.Text;

Verwenden Sie dann diesen Code

string input = "some text"; 
byte[] array = Encoding.ASCII.GetBytes(input);

Hoffe es zu beheben!


42

Sie können auch eine Erweiterungsmethode verwenden , um dem stringTyp eine Methode wie folgt hinzuzufügen :

static class Helper
{
   public static byte[] ToByteArray(this string str)
   {
      return System.Text.Encoding.ASCII.GetBytes(str);
   }
}

Und benutze es wie folgt:

string foo = "bla bla";
byte[] result = foo.ToByteArray();

12
Ich würde diese Methode umbenennen, um die Tatsache einzuschließen, dass sie ASCII-Codierung verwendet. So etwas wie ToASCIIByteArray. Ich hasse es, wenn ich herausfinde, dass eine Bibliothek, die ich verwende, ASCII verwendet, und ich gehe davon aus, dass sie UTF-8 oder etwas Moderneres verwendet.
T Blank

29
var result = System.Text.Encoding.Unicode.GetBytes(text);

3
Dies sollte die akzeptierte Antwort sein, da die anderen Antworten ASCII vorschlagen, aber die Codierung ist entweder Unicode (was es UTF16 ist) oder UTF8.
Abel

26
static byte[] GetBytes(string str)
{
     byte[] bytes = new byte[str.Length * sizeof(char)];
     System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
     return bytes;
}

static string GetString(byte[] bytes)
{
     char[] chars = new char[bytes.Length / sizeof(char)];
     System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
     return new string(chars);
}

Dies schlägt bei Zeichen fehl, die in den Bereich der Ersatzpaare fallen. GetBytes verfügt über ein Byte-Array, bei dem am Ende ein normales Zeichen pro Ersatzpaar fehlt. Der GetString hat am Ende leere Zeichen. Die einzige Möglichkeit wäre, wenn die Standardeinstellung von Microsoft UTF32 wäre oder wenn Zeichen im Ersatzpaarbereich nicht zulässig wären. Oder gibt es etwas, das ich nicht sehe? Der richtige Weg ist, die Zeichenfolge in Bytes zu "codieren".
Gerard ONeill

Richtig, für einen größeren Bereich können Sie etwas verwenden, das der Lösung von #Timothy Randall ähnelt: using System; using System.Text; Namespace Beispiel {public class Programm {public static void Main (string [] args) {string s1 = "Hallo Welt"; Zeichenfolge s2 = "שלום עולם"; Zeichenfolge s3 = "你好 你好 世界 世界"; Console.WriteLine (Encoding.UTF8.GetString (Encoding.UTF8.GetBytes (s1))); Console.WriteLine (Encoding.UTF8.GetString (Encoding.UTF8.GetBytes (s2))); Console.WriteLine (Encoding.UTF8.GetString (Encoding.UTF8.GetBytes (s3))); }}}
Eran Yogev

17

Warum Encoding.Default nicht verwendet werden sollte ...

@ Randalls Antwort verwendet Encoding.Default, aber Microsoft warnt davor :

Verschiedene Computer können standardmäßig unterschiedliche Codierungen verwenden, und die Standardcodierung kann sich auf einem einzelnen Computer ändern. Wenn Sie die Standardcodierung verwenden, um Daten zu codieren und zu decodieren, die zwischen Computern gestreamt oder zu unterschiedlichen Zeiten auf demselben Computer abgerufen werden, werden diese Daten möglicherweise falsch übersetzt. Darüber hinaus verwendet die von der Default-Eigenschaft zurückgegebene Codierung den Best-Fit-Fallback, um nicht unterstützte Zeichen Zeichen zuzuordnen, die von der Codepage unterstützt werden. Aus diesen Gründen wird die Verwendung der Standardcodierung nicht empfohlen. Um sicherzustellen, dass codierte Bytes ordnungsgemäß decodiert werden, sollten Sie eine Unicode-Codierung verwenden, z. B. UTF8Encoding oder UnicodeEncoding. Sie können auch ein übergeordnetes Protokoll verwenden, um sicherzustellen, dass für die Codierung und Decodierung dasselbe Format verwendet wird.

Verwenden Sie Encoding.Default.WindowsCodePage(in meinem Fall 1250 - und leider gibt es keine vordefinierte Klasse der CP1250-Codierung, aber das Objekt kann als abgerufen werden Encoding.GetEncoding(1250)), um die Standardcodierung zu überprüfen .

Encoding.ASCII ist 7bit, also funktioniert es auch nicht, in meinem Fall:

byte[] pass = Encoding.ASCII.GetBytes("šarže");
Console.WriteLine(Encoding.ASCII.GetString(pass)); // ?ar?e

... und warum stattdessen UTF-8-Codierung verwendet werden sollte ...

Die Standardcodierung ist irreführend: .NET verwendet UTF-8 überall als Standard (8-Bit-Codierungen sind Ende des 20. Jahrhunderts veraltet, überprüfen Sie dh Console.OutputEncoding.EncodingName*), sodass jede im Code definierte Konstante standardmäßig UTF-8-codiert ist Dieser sollte verwendet werden, es sei denn, die Datenquelle hat eine andere Codierung.

* Dies ist in meinem Fall UTF-8, was eine direkte Lüge ist: chcpVon der Windows-Konsole (cmd) wird 852 zurückgegeben - und dies sollte nicht geändert werden, da bei lokalisierten Systembefehlen (wie Ping) diese Codepage fest codiert ist

Befolgen Sie die Empfehlung von Microsoft:

var utf8 = new UTF8Encoding();
byte[] pass = utf8.GetBytes("šarže");
Console.WriteLine(utf8.GetString(pass)); // šarže

Encoding.UTF8 Von anderen empfohlen wird eine Instanz der UTF-8-Codierung, die auch direkt oder als verwendet werden kann

var utf8 = Encoding.UTF8 as UTF8Encoding;

... aber es wird nicht immer verwendet

Die Codierung für Byte-Arrays sollte in westlichen Ländern in Unicode "nur funktionieren". Sobald Sie Ihr Programm jedoch in weniger unterstützte Regionen (wie hier in Osteuropa) verschieben, ist dies ein echtes Chaos: In der Tschechischen Republik wird Windows standardmäßig verwendet (im Jahr 2020!) MS nicht Standard 852 (auch bekannt als Latin-2) für Konsole, 1250 als Windows OEM, UTF-8 (65001) als .NET (und andere) neuer Standard und wir sollten bedenken, dass einige westliche EU 8bit Daten sind noch im Jahr 1252, während der alte 8-Bit-Weststandard für Osteuropa ISO-8859-2 war (auch bekannt als Latin-2, aber NICHT das gleiche Latin-2 wie 852). Die Verwendung von ASCII bedeutet Text voller Tofu und '?' Hier. Stellen Sie UTF-8 also bis zur Hälfte des 21. Jahrhunderts explizit ein .


12

Aufbauend auf Alis Antwort würde ich eine Erweiterungsmethode empfehlen, mit der Sie optional die Codierung übergeben können, die Sie verwenden möchten:

using System.Text;
public static class StringExtensions
{
    /// <summary>
    /// Creates a byte array from the string, using the 
    /// System.Text.Encoding.Default encoding unless another is specified.
    /// </summary>
    public static byte[] ToByteArray(this string str, Encoding encoding = Encoding.Default)
    {
        return encoding.GetBytes(str);
    }
}

Und benutze es wie folgt:

string foo = "bla bla";

// default encoding
byte[] default = foo.ToByteArray();

// custom encoding
byte[] unicode = foo.ToByteArray(Encoding.Unicode);

2
Beachten Sie, dass die Verwendung Encoding encoding = Encoding.Defaultzu einem Fehler bei der Kompilierung führt:CS1736 Default parameter value for 'encoding' must be a compile-time constant
Douglas Gaskell

11

Der folgende Ansatz funktioniert nur, wenn die Zeichen 1 Byte groß sind. (Standard-Unicode funktioniert nicht, da es 2 Bytes sind)

public static byte[] ToByteArray(string value)
{            
    char[] charArr = value.ToCharArray();
    byte[] bytes = new byte[charArr.Length];
    for (int i = 0; i < charArr.Length; i++)
    {
        byte current = Convert.ToByte(charArr[i]);
        bytes[i] = current;
    }

    return bytes;
}

Einfach halten


charund stringsind per Definition UTF-16.
Tom Blodget

Ja, der Standardwert ist UTF-16. Ich mache keine Annahmen über die Codierung der Eingabezeichenfolge.
Mandar Sudame

Es gibt keinen Text, sondern verschlüsselten Text. Ihre Eingabe ist vom Typ stringund daher UTF-16. UTF-16 ist nicht die Standardeinstellung. es gibt keine Wahl. Sie teilen sich dann in char[]UTF-16-Codeeinheiten auf. Sie rufen dann Convert.ToByte (Char) auf , das zufällig U + 0000 in U + 00FF in ISO-8859-1 konvertiert und alle anderen Codepunkte entstellt.
Tom Blodget

Macht Sinn. Danke für die Klarstellung. Aktualisiere meine Antwort.
Mandar Sudame

1
Ich denke, Ihnen fehlen noch einige wesentliche Punkte. Konzentrieren Sie sich darauf char, 16 Bit zu sein und die Convert.ToByte()Hälfte davon wegzuwerfen.
Tom Blodget

10

benutze das

byte[] myByte= System.Text.ASCIIEncoding.Default.GetBytes(myString);

6

Eine Verfeinerung der Bearbeitung von JustinStolle (Eran Yogevs Verwendung von BlockCopy).

Die vorgeschlagene Lösung ist in der Tat schneller als die Verwendung der Codierung. Das Problem ist, dass es nicht zum Codieren von Byte-Arrays mit ungleicher Länge funktioniert. Wie angegeben, wird eine Out-of-Bound-Ausnahme ausgelöst. Wenn Sie die Länge um 1 erhöhen, bleibt beim Dekodieren aus einem String ein nachfolgendes Byte übrig.

Für mich kam die Notwendigkeit , als ich zu kodieren von wollte DataTablezu JSON. Ich suchte nach einer Möglichkeit, Binärfelder in Strings zu codieren und von String zurück zu zu decodieren byte[].

Ich habe daher zwei Klassen erstellt - eine, die die obige Lösung umschließt (beim Codieren aus Zeichenfolgen ist dies in Ordnung, da die Längen immer gerade sind), und eine andere, die das byte[]Codieren übernimmt .

Ich habe das Problem der ungleichmäßigen Länge gelöst, indem ich ein einzelnes Zeichen hinzugefügt habe, das mir sagt, ob die ursprüngliche Länge des binären Arrays ungerade ('1') oder gerade ('0') war.

Wie folgt:

public static class StringEncoder
{
    static byte[] EncodeToBytes(string str)
    {
        byte[] bytes = new byte[str.Length * sizeof(char)];
        System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
        return bytes;
    }
    static string DecodeToString(byte[] bytes)
    {
        char[] chars = new char[bytes.Length / sizeof(char)];
        System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
        return new string(chars);
    }
}

public static class BytesEncoder
{
    public static string EncodeToString(byte[] bytes)
    {
        bool even = (bytes.Length % 2 == 0);
        char[] chars = new char[1 + bytes.Length / sizeof(char) + (even ? 0 : 1)];
        chars[0] = (even ? '0' : '1');
        System.Buffer.BlockCopy(bytes, 0, chars, 2, bytes.Length);

        return new string(chars);
    }
    public static byte[] DecodeToBytes(string str)
    {
        bool even = str[0] == '0';
        byte[] bytes = new byte[(str.Length - 1) * sizeof(char) + (even ? 0 : -1)];
        char[] chars = str.ToCharArray();
        System.Buffer.BlockCopy(chars, 2, bytes, 0, bytes.Length);

        return bytes;
    }
}

4

Diese Frage wurde ausreichend oft beantwortet, aber mit C # 7.2 und der Einführung des Span-Typs gibt es eine schnellere Möglichkeit, dies in unsicherem Code zu tun:

public static class StringSupport
{
    private static readonly int _charSize = sizeof(char);

    public static unsafe byte[] GetBytes(string str)
    {
        if (str == null) throw new ArgumentNullException(nameof(str));
        if (str.Length == 0) return new byte[0];

        fixed (char* p = str)
        {
            return new Span<byte>(p, str.Length * _charSize).ToArray();
        }
    }

    public static unsafe string GetString(byte[] bytes)
    {
        if (bytes == null) throw new ArgumentNullException(nameof(bytes));
        if (bytes.Length % _charSize != 0) throw new ArgumentException($"Invalid {nameof(bytes)} length");
        if (bytes.Length == 0) return string.Empty;

        fixed (byte* p = bytes)
        {
            return new string(new Span<char>(p, bytes.Length / _charSize));
        }
    }
}

Beachten Sie, dass die Bytes eine UTF-16-codierte Zeichenfolge darstellen (in C # -Land "Unicode" genannt).

Einige schnelle Benchmarking-Tests zeigen, dass die oben genannten Methoden ungefähr fünfmal schneller sind als die Implementierungen Encoding.Unicode.GetBytes (...) / GetString (...) für mittelgroße Zeichenfolgen (30-50 Zeichen) und sogar noch schneller für größere Zeichenfolgen. Diese Methoden scheinen auch schneller zu sein als die Verwendung von Zeigern mit Marshal.Copy (..) oder Buffer.MemoryCopy (...).


4

Wenn das Ergebnis von 'searchResult.Properties ["user"] [0]' eine Zeichenfolge ist:

if ( ( searchResult.Properties [ "user" ].Count > 0 ) ) {

   profile.User = System.Text.Encoding.UTF8.GetString ( searchResult.Properties [ "user" ] [ 0 ].ToCharArray ().Select ( character => ( byte ) character ).ToArray () );

}

Der entscheidende Punkt ist, dass die Konvertierung eines Strings in ein Byte [] mit LINQ erfolgen kann:

.ToCharArray ().Select ( character => ( byte ) character ).ToArray () )

Und umgekehrt:

.Select ( character => ( char ) character ).ToArray () )

3

Hat jemand einen Grund, dies nicht zu tun?

mystring.Select(Convert.ToByte).ToArray()

10
Convert.ToByte(char)funktioniert nicht so wie du denkst. Das Zeichen '2'wird in das Byte konvertiert 2, nicht in das Byte, das das Zeichen darstellt '2'. Verwenden Sie mystring.Select(x => (byte)x).ToArray()stattdessen.
Jack


2

Sie können die MemoryMarshal-API verwenden , um eine sehr schnelle und effiziente Konvertierung durchzuführen. Stringwird implizit umgewandelt ReadOnlySpan<byte>, als MemoryMarshal.Castentweder Span<byte>oder ReadOnlySpan<byte>als Eingabeparameter akzeptiert .

public static class StringExtensions
{
    public static byte[] ToByteArray(this string s) => s.ToByteSpan().ToArray(); //  heap allocation, use only when you cannot operate on spans
    public static ReadOnlySpan<byte> ToByteSpan(this string s) => MemoryMarshal.Cast<char, byte>(s);
}

Der folgende Benchmark zeigt den Unterschied:

Input: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,"

|                       Method |       Mean |     Error |    StdDev |  Gen 0 | Gen 1 | Gen 2 | Allocated |
|----------------------------- |-----------:|----------:|----------:|-------:|------:|------:|----------:|
| UsingEncodingUnicodeGetBytes | 160.042 ns | 3.2864 ns | 6.4099 ns | 0.0780 |     - |     - |     328 B |
| UsingMemoryMarshalAndToArray |  31.977 ns | 0.7177 ns | 1.5753 ns | 0.0781 |     - |     - |     328 B |
|           UsingMemoryMarshal |   1.027 ns | 0.0565 ns | 0.1630 ns |      - |     - |     - |         - |

0

Diese Arbeit für mich, danach konnte ich mein Bild in ein Bytea-Feld in meiner Datenbank konvertieren.

using (MemoryStream s = new MemoryStream(DirEntry.Properties["thumbnailphoto"].Value as byte[]))
{
    return s.ToArray();
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.