C # içindeki diziyi bayt dizisine dönüştürme


670

Bir şey VB C # dönüştürüyorum. Bu ifadenin sözdizimiyle ilgili bir sorun mu yaşıyorsunuz:

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

Sonra aşağıdaki hataları görüyorum:

Bağımsız değişken 1: 'nesne'den' bayt [] 'biçimine dönüştürülemiyor

'System.Text.Encoding.GetString (byte [])' için en çok yüklenmiş yöntem eşleşmesi bazı geçersiz argümanlara sahip

Bu yazıya dayalı kodu düzeltmeye çalıştım , ancak yine de başarı yok

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

Herhangi bir öneri?


1
Ne tür searchResult.Properties["user"][0]? byte[]İlk döküm yapmayı deneyin
mshsayem

mshsayem gittiğim yere gitti. (byte[])SearchResult öğesindeki bir oyuncu kadrosu eksik mi?
Harrison

2
Ne tür Properties["user"][0]olduğunu bulmanız gerekir . Bir bayt dizisi olduğundan eminseniz, bu şekilde yayınlayabilirsinizprofile.User = System.Text.Encoding.UTF8.GetString((byte[])searchResult.Properties["user"][0]);
keyboardP

1
Tüm bu yaygara gerekmediği ortaya çıkıyor. Sonuçta kullanıcı adı kodlanmadan getirilebilir.
Nouptime

3
Neden doğru cevabı seçmiyorsun?
Ali

Yanıtlar:


1189

Zaten bir bayt diziniz varsa, o bayt dizisine dönüştürmek için ne tür kodlamanın kullanıldığını bilmeniz gerekir.

Örneğin, bayt dizisi şu şekilde oluşturulduysa:

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

Bunu aşağıdaki gibi bir dizeye dönüştürmeniz gerekir:

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

Devralınan kodda bulabiliyorsanız, bayt dizisini oluşturmak için kullanılan kodlama ayarlanmalıdır.


3
Timothy, VB kodunu inceledim ve bahsettiğiniz gibi bir bayt dizisi bulamıyorum.
Nouptime

Arama sonucunuzda, Properties özelliğinin türü nedir?
Timothy Randall

Tüm görebildiğim bir dize olarak Özellikler bağlı bir sayı öğeleri olmasıdır. Bana sorduğun şeyin bu olup olmadığından emin değilim.
nouptime

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

1
Benim durumum için Encoding.Unicode.GetBytes'in çalıştığını buldum (ancak ASCII işe yaramadı)
Jeff

106

Her şeyden önce, System.Textad alanını ekleyin

using System.Text;

Sonra bu kodu kullanın

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

Düzeltmek dileğiyle!


42

Ayrıca , türe aşağıdaki gibi bir yöntem eklemek için bir Uzantı Yöntemi kullanabilirsiniz string:

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

Ve aşağıdaki gibi kullanın:

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

12
ASCII kodlaması kullanıyor aslında dahil etmek için bu yöntemi yeniden adlandırmak. Gibi bir şey ToASCIIByteArray. Kullandığım kütüphanenin ASCII kullandığını öğrendiğimde nefret ediyorum ve UTF-8 ya da daha modern bir şey kullandığını varsayıyorum.
T Blank

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

3
Diğer cevaplar ASCII'yi önerdiğinden bu kabul edilen cevap olmalıdır, ancak kodlama Unicode (UTF16) ya da UTF8'dir.
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);
}

Bu vekil çifti aralığına düşen karakterler için başarısız olacaktır. GetBytes vekil çifti başına bir normal karakter kaçırılan bir bayt dizisine sahip olacaktır. GetString sonunda boş grafikler olacak. Çalışmanın tek yolu, microsoft'un varsayılan değeri UTF32 ise veya vekil çifti aralığındaki karakterlere izin verilmiyorsa. Yoksa görmediğim bir şey mi var? Doğru yol dizgiyi baytlara 'kodlamak'tır.
Gerard ONeill

Doğru, daha geniş bir aralık için #Timothy Randall'ın çözümüne benzer bir şey kullanabilirsiniz: using System; System.Text kullanarak; ad alanı Örnek {public class Program {public static void Main (string [] args) {string s1 = "Merhaba Dünya"; string s2 = "שלום עולם"; string s3 = "你好 , 世界!"; Console.WriteLine (Encoding.UTF8.GetString (Encoding.UTF8.GetBytes (s 1))); Console.WriteLine (Encoding.UTF8.GetString (Encoding.UTF8.GetBytes (s2))); Console.WriteLine (Encoding.UTF8.GetString (Encoding.UTF8.GetBytes (s3))); }}}
Eran Yogev

17

Neden Kodlama Varsayılan kullanılmamalıdır ...

@ Randall'ın cevabı kullanır Encoding.Default, ancak Microsoft buna karşı bir uyarı yapar :

Farklı bilgisayarlar varsayılan olarak farklı kodlamalar kullanabilir ve varsayılan kodlama tek bir bilgisayarda değişebilir. Bilgisayarlar arasında aktarılan veya aynı bilgisayarda farklı zamanlarda alınan verileri kodlamak ve kodunu çözmek için Varsayılan kodlamayı kullanırsanız, bu veriler yanlış çevrilebilir. Ayrıca, Default özelliği tarafından döndürülen kodlama, desteklenmeyen karakterleri kod sayfası tarafından desteklenen karakterlerle eşleştirmek için en uygun yedeklemeyi kullanır. Bu nedenlerle, varsayılan kodlamanın kullanılması önerilmez. Kodlanmış baytların doğru bir şekilde kodunun çözülmesini sağlamak için UTF8Encoding veya UnicodeEncoding gibi bir Unicode kodlaması kullanmalısınız. Aynı formatın kodlama ve kod çözme için kullanıldığından emin olmak için daha üst düzey bir protokol de kullanabilirsiniz.

Varsayılan kodlamanın ne olduğunu kontrol etmek için kullanın Encoding.Default.WindowsCodePage(benim durumumda 1250 - ve ne yazık ki, önceden tanımlanmış bir CP1250 kodlaması sınıfı yoktur, ancak nesne olarak alınabilir Encoding.GetEncoding(1250)).

Encoding.ASCII 7bit, bu yüzden benim durumumda da çalışmıyor:

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

... ve neden UTF-8 kodlaması kullanılmalı ...

Varsayılan kodlama yanıltıcıdır: .NET, gerçek varsayılan olarak UTF-8'i her yerde kullanır (8 bit kodlamalar 20. yüzyılın sonunda geçersiz hale gelir, yani kontrol edin Console.OutputEncoding.EncodingName*), böylece kodda tanımladığınız her sabit varsayılan olarak UTF-8 olarak kodlanır - yani bu, veri kaynağı farklı kodlamada olmadığı sürece kullanılmalıdır.

* Bu benim durumumda doğrudan yalan olan UTF-8: chcpWindows konsolundan (cmd) 852 döndürür - ve bu değiştirilmemelidir, çünkü yerelleştirilmiş sistem komutları (ping gibi) bu kod sayfasına sabit kodlanmıştır

Microsoft'un önerisine göre:

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

Encoding.UTF8 başkaları tarafından önerilen UTF-8 kodlamasının bir örneğidir ve doğrudan veya

var utf8 = Encoding.UTF8 as UTF8Encoding;

... ama her zaman kullanılmaz

Bayt dizileri için kodlama, Batı ülkelerinde Unicode'da "sadece çalışmalıdır", ancak programınızı daha az desteklenen bölgelere (Doğu Avrupa'daki gibi) taşıdığınızda, bu gerçek bir karışıklıktır: Çek Cumhuriyeti'nde Windows varsayılanları kullanımı (2020'de!) Konsol için MS standart dışı 852 (diğer adıyla Latin-2), Windows OEM olarak 1250, .NET (ve diğerleri) olarak UTF-8 (65001) yeni varsayılan ve bazı batı AB 8 bitlerinin veriler hala 1252'de iken, Doğu Avrupa için eski 8 bit batı standardı ISO-8859-2 idi (Latin-2 olarak da bilinir, ancak 852 ile aynı Latin-2 DEĞİLDİR). ASCII kullanmak tofu ve '?' buraya. Bu yüzden 21. yüzyılın yarısına kadar lütfen UTF-8'i açıkça ayarlayın .


12

Ali'nin cevabını inşa ederek, isteğe bağlı olarak kullanmak istediğiniz kodlamayı geçirmenizi sağlayan bir uzantı yöntemi öneriyorum:

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);
    }
}

Ve aşağıdaki gibi kullanın:

string foo = "bla bla";

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

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

2
Encoding encoding = Encoding.DefaultDerleme zamanı hatasıyla sonuçların kullanıldığını unutmayın :CS1736 Default parameter value for 'encoding' must be a compile-time constant
Douglas Gaskell

11

Aşağıdaki yaklaşım yalnızca karakter 1 bayt olduğunda işe yarar. (Varsayılan unicode 2 bayt olduğu için çalışmaz)

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;
}

Basit tutmak


charve stringtanım gereği UTF-16'dır.
Tom Blodget

Evet, varsayılan UTF-16'dır. Giriş dizesinin kodlaması hakkında herhangi bir varsayım yapmıyorum.
Mandar Sudame

Metin yok ama kodlanmış metin var. Girişiniz tiptir stringve bu nedenle UTF-16'dır. UTF-16 varsayılan değer değildir; bu konuda başka seçenek yok. Daha sonra char[]UTF-16 kod birimlerine bölündünüz. Daha sonra U + 0000'ı U + 00FF'yi ISO- 8859-1'e dönüştüren ve diğer kod noktalarını yöneten Convert.ToByte (Char) öğesini çağırırsınız .
Tom Blodget

Mantıklı. Açıklama için teşekkürler. Cevabım güncelleniyor.
Mandar Sudame

1
Sanırım hala birkaç önemli noktayı kaçırıyorsunuz. char16 bit olmaya ve Convert.ToByte()yarısını atmaya odaklanın .
Tom Blodget

10

bunu kullan

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

6

JustinStolle'ın (Eran Yogev'in BlockCopy'yi kullanması) düzenlemesine bir ayrıntı.

Önerilen çözüm gerçekten Kodlama kullanmaktan daha hızlıdır. Sorun, düzensiz uzunluktaki bayt dizilerini kodlamak için işe yaramamasıdır. Verildiği gibi, sınır dışı bir istisna oluşturur. Uzunluğun 1 arttırılması, dizeden kod çözülürken arka bayt bırakır.

Ben gelen kodlamak isteyince Benim için ihtiyaç geldi DataTableetmek JSON. Ben ikili alanları dizelere kodlamak ve geri dize kodunu çözmek için bir yol arıyordu byte[].

Bu nedenle iki sınıf oluşturdum - biri yukarıdaki çözümü sarar (dizelerden kodlama yaparken iyidir, çünkü uzunluklar her zaman eşittir) ve diğeri byte[]kodlamayı işler .

İkili dizinin orijinal uzunluğunun tek ('1') veya çift ('0') olduğunu söyleyen tek bir karakter ekleyerek düzensiz uzunluk sorununu çözdüm

Aşağıdaki gibi:

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

Bu soru birçok kez yeterince cevaplanmıştır, ancak C # 7.2 ve Span türünün tanıtımı ile, güvensiz kodda bunu yapmanın daha hızlı bir yolu vardır:

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));
        }
    }
}

Baytların UTF-16 kodlu bir dizeyi (C # land'da "Unicode" olarak adlandırılır) temsil ettiğini unutmayın.

Bazı hızlı kıyaslama, yukarıdaki yöntemlerin Orta Kodlama (30-50 karakter) için Kodlama.Unicode.GetBytes (...) / GetString (...) uygulamalarının kabaca 5 kat daha hızlı ve daha büyük dizeler için daha hızlı olduğunu gösterir. Bu yöntemler aynı zamanda Marshal.Copy (..) veya Buffer.MemoryCopy (...) ile işaretçiler kullanmaktan daha hızlı görünmektedir.


4

'SearchResult.Properties ["user"] [0]' sonucu bir dizedir:

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

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

}

Önemli nokta, bir dizenin bayta [] dönüştürülmesinin LINQ kullanılarak yapılabilmesidir:

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

Ve tersi:

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

3

Bunu yapmamak için herhangi bir sebep gören var mı?

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

10
Convert.ToByte(char)düşündüğünüz gibi çalışmıyor. Karakter '2', karakteri 2temsil eden bayta değil, bayta dönüştürülür '2'. mystring.Select(x => (byte)x).ToArray()Bunun yerine kullanın .
Jack


2

MemoryMarshal API'sini çok hızlı ve verimli bir dönüşüm gerçekleştirmek için kullanabilirsiniz . Stringörtülü ReadOnlySpan<byte>olarak MemoryMarshal.Castkabul parametresi olarak Span<byte>veya ReadOnlySpan<byte>bir girdi parametresi olarak kullanılır.

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);
}

Aşağıdaki kıyaslama farkı göstermektedir:

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

Bu iş benim için, daha sonra benim veritabanındaki bir bytea alanına resim koymak olabilir.

using (MemoryStream s = new MemoryStream(DirEntry.Properties["thumbnailphoto"].Value as byte[]))
{
    return s.ToArray();
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.