it-swarm.dev

El ile bir kodlama belirtmeden, C # 'daki dizgelerin tutarlı bir bayt temsilini nasıl elde ederim?

Belirli bir kodlamayı el ile belirtmeden string öğesini .NET'te (c #) bir byte[] dosyasına nasıl dönüştürebilirim?

Dizeyi şifreleyeceğim. Dönüştürmeden şifreleyebilirim, ancak neden kodlamanın burada çalmaya geldiğini bilmek istiyorum.

Ayrıca, kodlama neden dikkate alınmalıdır? Dize hangi baytların depolandığını basitçe öğrenemiyorum? Neden karakter kodlamasına bağımlılık var?

2055
Agnel Kurian

Buradaki cevapların aksine, kodlama konusunda endişelenmenize gerek yokeğer/ baytların yorumlanması gerekmez!

Bahsettiğiniz gibi, hedefiniz basitçe, "dizginin depoladığı baytları almak".
(Ve tabii ki, dizgiyi baytlardan yeniden yapılandırabilmek için.)

Bu amaçlar için, dürüstçe söylüyorum ki {değil insanlar size neden kodlamalara ihtiyacınız olduğunu söylemeye devam ediyorlar. Bunun için kodlamalar konusunda endişelenmenize kesinlikle gerek yok.

Bunun yerine sadece bunu yapın:

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

Programınız (veya diğer programlarınız) bir şekilde yorumlama byte'ları denemeye çalışmadıkça, açıkça yapmak istediğinizden bahsetmediğiniz baytları, o zaman bu yaklaşımda hiçbir şey yanlış ! Kodlamalar konusunda endişelenmek hayatınızı gerçek bir sebep olmadan daha karmaşık hale getiriyor.

Bu yaklaşımın ek faydası:

Dize geçersiz karakterler içerip içermediği önemli değildir, çünkü verileri yine de alabilirsiniz ve orijinal dizeyi yine de yeniden yapabilirsiniz!

Aynı şekilde kodlanacak ve kodu çözülecektir, çünkü siz sadece baytlara bakıyorsunuz.

Belirli bir kodlama kullandıysanız, geçersiz karakterleri kodlama/kod çözme konusunda size sorun çıkarırdı.

1793
Mehrdad

Bu, dizginizin kodlamasına bağlıdır ( ASCII , UTF-8 , ...).

Örneğin:

byte[] b1 = System.Text.Encoding.UTF8.GetBytes (myString);
byte[] b2 = System.Text.Encoding.ASCII.GetBytes (myString);

Kodlamanın neden önemli olduğunu gösteren küçük bir örnek:

string pi = "\u03a0";
byte[] ascii = System.Text.Encoding.ASCII.GetBytes (pi);
byte[] utf8 = System.Text.Encoding.UTF8.GetBytes (pi);

Console.WriteLine (ascii.Length); //Will print 1
Console.WriteLine (utf8.Length); //Will print 2
Console.WriteLine (System.Text.Encoding.ASCII.GetString (ascii)); //Will print '?'

ASCII sadece özel karakterlerle başa çıkmak için donanımlı değildir.

Dahili olarak, .NET çerçevesi dizeleri temsil etmek için UTF-16 öğesini kullanır, bu nedenle yalnızca .NET'in kullandığı tam baytları almak istiyorsanız System.Text.Encoding.Unicode.GetBytes (...) öğesini kullanın.

Daha fazla bilgi için bkz..NET Framework'deki Karakter Kodlaması(MSDN).

1079
bmotmans

Kabul edilen cevap çok, çok karmaşık. Bunun için dahil edilen .NET sınıflarını kullanın:

const string data = "A string with international characters: Norwegian: ÆØÅæøå, Chinese: 喂 谢谢";
var bytes = System.Text.Encoding.UTF8.GetBytes(data);
var decoded = System.Text.Encoding.UTF8.GetString(bytes);

Gerekirse tekerleği yeniden icat etme ...

267
BinaryFormatter bf = new BinaryFormatter();
byte[] bytes;
MemoryStream ms = new MemoryStream();

string orig = "喂 Hello 谢谢 Thank You";
bf.Serialize(ms, orig);
ms.Seek(0, 0);
bytes = ms.ToArray();

MessageBox.Show("Original bytes Length: " + bytes.Length.ToString());

MessageBox.Show("Original string Length: " + orig.Length.ToString());

for (int i = 0; i < bytes.Length; ++i) bytes[i] ^= 168; // pseudo encrypt
for (int i = 0; i < bytes.Length; ++i) bytes[i] ^= 168; // pseudo decrypt

BinaryFormatter bfx = new BinaryFormatter();
MemoryStream msx = new MemoryStream();            
msx.Write(bytes, 0, bytes.Length);
msx.Seek(0, 0);
string sx = (string)bfx.Deserialize(msx);

MessageBox.Show("Still intact :" + sx);

MessageBox.Show("Deserialize string Length(still intact): " 
    + sx.Length.ToString());

BinaryFormatter bfy = new BinaryFormatter();
MemoryStream msy = new MemoryStream();
bfy.Serialize(msy, sx);
msy.Seek(0, 0);
byte[] bytesy = msy.ToArray();

MessageBox.Show("Deserialize bytes Length(still intact): " 
   + bytesy.Length.ToString());
110
Michael Buen

Kodlamayı hesaba katmanız gerekir, çünkü 1 karakter 1 veya daha fazla byte (en fazla 6) ile gösterilebilir ve farklı kodlamalar bu baytları farklı şekilde ele alır.

Joel bu konuda bir ilanı var:

Mutlak Minimum Her Yazılım Geliştiricisi Kesinlikle, Unicode ve Karakter Kümelerini Mutlak Olarak Bilmeli (Mazeret Yok!)

87

Bu popüler bir soru. Soru yazarının ne sorduğunu ve bunun en yaygın ihtiyaç olan şeyden farklı olduğunu anlamak önemlidir. İhtiyaç duyulmadığında kodun kötüye kullanılmasını önlemek için daha sonra cevapladım.

Ortak ihtiyaç

Her dizgenin bir karakter seti ve kodlaması vardır. Bir System.String nesnesini System.Byte dizisine dönüştürdüğünüzde hala bir karakter kümesi ve kodlaması vardır. Çoğu kullanımda, hangi karakter kümesine ve kodlamasına ihtiyacınız olduğunu bilirsiniz ve .NET, "dönüşümle kopyalamayı" kolaylaştırır. Sadece uygun Encoding sınıfını seçin.

// using System.Text;
Encoding.UTF8.GetBytes(".NET String to byte array")

Dönüşümün, hedef karakter kümesinin veya kodlamanın kaynakta bulunan bir karakteri desteklemediği durumları ele alması gerekebilir. Bazı seçenekleriniz var: istisna, yerine koyma veya atlama. Varsayılan politika bir '?' Yerine koymaktır.

// using System.Text;
var text = Encoding.ASCII.GetString(Encoding.ASCII.GetBytes("You win €100")); 
                                                      // -> "You win ?100"

Açıkçası, dönüşümler mutlaka kayıpsız değildir!

Not: System.String için kaynak karakter kümesi Unicode'dur.

Tek kafa karıştıran şey, .NET'in o karakter kümesinin belirli bir kodlamasının adı için bir karakter kümesinin adını kullanmasıdır. Encoding.Unicode, Encoding.UTF16 olarak adlandırılmalıdır.

Çoğu kullanım için budur. İhtiyacınız olan buysa, burada okumayı bırakın. Eğlenceye bakın Joel Spolsky makalesi Eğer bir kodlamanın ne olduğunu anlamıyorsanız.

Özel ihtiyaç

Şimdi, soru yazarı, “Her dize bir bayt dizisi olarak saklanır, değil mi? Neden sadece bu baytlara sahip olamıyorum?” Diye soruyor.

Dönüşüm istemiyor.

C # spec dizininden:

C # ile karakter ve string işleme Unicode kodlamasını kullanır. Karakter türü bir UTF-16 kod birimini ve dizi türü bir UTF-16 kod birimi dizisini temsil eder.

Öyleyse, null dönüşümünü istersek (yani, UTF-16'dan UTF-16'ya) sorarsak, istenen sonucu elde edeceğimizi biliyoruz:

Encoding.Unicode.GetBytes(".NET String to byte array")

Ancak kodlamalardan kaçınmak için başka bir yolla yapmalıyız. Bir ara veri tipi kabul edilebilir ise, bunun için kavramsal bir kısayol vardır:

".NET String to byte array".ToCharArray()

Bu bize istenen veri tipini getirmiyor ama Mehrdad'ın cevabı , BlockCopy kullanarak bu Char dizisini bir Byte dizisine nasıl dönüştürebileceğini gösteriyor. Ancak, bu dize iki kez kopyalar! Ayrıca, kodlamaya özel bir kod kullanır: datatype System.Char.

String'in depolandığı gerçek baytlara ulaşmanın tek yolu bir işaretçi kullanmaktır. fixed ifadesi, değerlerin adresinin alınmasına izin verir. C # spec'tan:

[String] türünde bir ifade için, ... başlatıcı, dizedeki ilk karakterin adresini hesaplar.

Bunu yapmak için, derleyici, kod yazar, RuntimeHelpers.OffsetToStringData ile string nesnesinin diğer kısımlarına atlar. Böylece, ham baytları almak için, sadece dizeye bir işaretçi oluşturun ve gereken bayt sayısını kopyalayın.

// using System.Runtime.InteropServices
unsafe byte[] GetRawBytes(String s)
{
    if (s == null) return null;
    var codeunitCount = s.Length;
    /* We know that String is a sequence of UTF-16 codeunits 
       and such codeunits are 2 bytes */
    var byteCount = codeunitCount * 2; 
    var bytes = new byte[byteCount];
    fixed(void* pRaw = s)
    {
        Marshal.Copy((IntPtr)pRaw, bytes, 0, byteCount);
    }
    return bytes;
}

@CodesInChaos'un işaret ettiği gibi, sonuç, makinenin türlüğüne bağlıdır. Ancak soru yazarı bununla ilgilenmiyor.

84
Tom Blodget

Sadece Mehrdrad'ın sesini cevapla çalıştığını göstermek için yaklaşımı eşleşmemiş vekil karakterleri (çoğu benim cevabımla aynı seviyeye geldiği, ancak herkesin eşit derecede suçlu olduğu, örneğin System.Text.Encoding.UTF8.GetBytes, System.Text.Encoding.Unicode.GetBytes; Bu kodlama yöntemleri, örneğin, yüksek vekil karakterleri d800 olarak kaldıramazlar ve sadece yüksek vekil karakterleri fffd değeriyle değiştiremezler):

using System;

class Program
{     
    static void Main(string[] args)
    {
        string t = "爱虫";            
        string s = "Test\ud800Test"; 

        byte[] dumpToBytes = GetBytes(s);
        string getItBack = GetString(dumpToBytes);

        foreach (char item in getItBack)
        {
            Console.WriteLine("{0} {1}", item, ((ushort)item).ToString("x"));
        }    
    }

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

Çıktı:

T 54
e 65
s 73
t 74
? d800
T 54
e 65
s 73
t 74

System.Text.Encoding.UTF8.GetBytes veya System.Text.Encoding.Unicode.GetBytes ile deneyin, yalnızca yüksek vekil karakterleri fffdile değiştirirler _

Her ne zaman bu soruda bir hareket varsa, hala eşleştirilmemiş yedek karakterler içerdiğinde dizeleri sürdürebilen bir serileştirici (Microsoft'tan veya 3. parti bileşeninden) düşünüyorum; Bunu şimdi ve sonra google ve sonra: seri hale getirme eşlenmemiş yedek karakter .NET. Bu beni uykuyu kaybetmeme zorlaştırmaz, ama her zaman ve her ne zaman birileri can sıkıcı bir yorumda bulunurken cevaplarıma kusurlu olduğunu söyleyen birileri vardır, ancak cevapları eşleştirilmemiş yedek karakterler söz konusu olduğunda cevapları eşit derecede kusurludur.

Kahretsin, Microsoft, BinaryFormatter dosyasında System.Buffer.BlockCopy kullanmalıydı.

谢谢!

43
Michael Buen

Sorunuzun ilk kısmı (baytların nasıl alınacağı) başkaları tarafından zaten yanıtlandı: System.Text.Encoding ad alanına bakın.

Takip eden sorunuzu ele alacağım: neden bir kodlama seçmeniz gerekiyor? Neden bunu string sınıfının kendisinden alamıyorsun?

Cevap iki bölümden oluşuyor.

Her şeyden önce, dahili olarak önemli değil dize sınıfı tarafından kullanılan baytlar ve ne zaman varsayalım, muhtemelen bir hataya neden oluyorsunuzdur.

Programınız tamamen .Net dünyasındaysa, bir ağ üzerinden veri gönderiyor olsanız bile, dizeler için bayt dizileri alma konusunda endişelenmenize gerek yoktur. Bunun yerine, veri iletimi konusunda endişelenmek için .Net Seri hale getirme kullanın. Artık gerçek baytlar için endişelenmeyin: Seri hale getirme biçimlendirici bunu sizin için yapar.

Öte yandan, bu baytları bir .Net serileştirilmiş akışından veri alacağını garanti edemeyeceğiniz bir yere gönderiyorsanız ne olur? Bu durumda kesinlikle kodlama konusunda endişelenmeniz gerekir, çünkü açıkçası bu harici sistem önemsiyor. Yine, dizge tarafından kullanılan dahili baytların önemi yoktur: bir kodlama seçmeniz gerekir, böylece alıcı tarafından bu kodlama hakkında açık olabilirsiniz, hatta dahili olarak .NET tarafından kullanılan aynı kodlama olsa bile.

Bu durumda, mümkünse bellekte string değişkeni tarafından saklanan gerçek byte'ları kullanmayı tercih edebileceğinizi, bayt akışınızı yaratan bazı işleri kurtarabileceğini düşünerek anlıyorum. Bununla birlikte, çıktınızın diğer ucunda anlaşıldığından emin olmak ve kodlamanızla açık bir şekilde zorunlu olmasını garanti etmekle kıyaslandığında, sizin için önemli değil. Ek olarak, dahili byte'larınızı gerçekten eşleştirmek istiyorsanız, zaten Unicode kodlamasını seçebilir ve bu performans tasarrufunu elde edebilirsiniz.

Bu da beni ikinci bölüme getiriyor ... Unicode kodlamasını seçerek is söyleyerek _Net'i seç. Alttaki baytları kullan. Bu kodlamayı seçmeniz gerekir, çünkü bazı yeni çıkmış Unicode-Plus'lar çıktığında .Net çalışma zamanı, programınızı bozmadan daha yeni ve daha iyi kodlama modelini kullanmak için ücretsiz olmalıdır. Ancak, şu an için (ve yakın gelecekte), yalnızca Unicode kodlamasını seçmek, istediğiniz şeyi verir.

Ayrıca, dizginizin kabloya yeniden yazılması gerektiğini ve bunun en azından bazı bit biçimlerinin çevrilmesini içerir eşleşen bir kodlama kullanıyorsanız bile. Bilgisayarın, Little vs Little Endian, ağ bayt sırası, paketleme, oturum bilgileri gibi şeyleri hesaba katması gerekir.

42
Joel Coehoorn

Bunu dene, çok daha az kod:

System.Text.Encoding.UTF8.GetBytes("TEST String");
38
Nathan

Şey, bütün cevapları okudum ve onlar kodlanmamış ya da eşlenmemiş vekilleri düşüren serileştirme hakkında bir tane kullanıyorlardı.

Dize, örneğin, SQL Server 'dan geldiğinde, bir bayt dizisinden, örneğin bir parola karma deposundan oluşturulduğu zaman kötüdür. Ondan bir şey bırakırsak, geçersiz bir karma depolar ve XML'de saklamak istiyorsak bozulmadan bırakmak isteriz (çünkü XML yazıcısı bulduğu eşlenmemiş herhangi bir vekilde istisna bırakmaktadır).

Bu yüzden Base64 / bayt dizilerini bu gibi durumlarda kodluyorum, ama hey, internette C # 'da bunun için tek bir çözüm var ve içinde hata var ve sadece tek bir yol var. böcek ve geri yazılı prosedür. İşte, gelecekteki çalışanlar:

public static byte[] StringToBytes(string str)
{
    byte[] data = new byte[str.Length * 2];
    for (int i = 0; i < str.Length; ++i)
    {
        char ch = str[i];
        data[i * 2] = (byte)(ch & 0xFF);
        data[i * 2 + 1] = (byte)((ch & 0xFF00) >> 8);
    }

    return data;
}

public static string StringFromBytes(byte[] arr)
{
    char[] ch = new char[arr.Length / 2];
    for (int i = 0; i < ch.Length; ++i)
    {
        ch[i] = (char)((int)arr[i * 2] + (((int)arr[i * 2 + 1]) << 8));
    }
    return new String(ch);
}
24
Gman

Ayrıca lütfen kodlamanın neden dikkate alınması gerektiğini açıklayın. Dize hangi baytların depolandığını basitçe öğrenemiyorum mu? Neden bu kodlamaya bağımlılık? !!!

Çünkü "dizginin baytı" diye bir şey yoktur.

Bir dize (veya daha genel olarak bir metin) karakterlerden oluşur: harfler, rakamlar ve diğer semboller. Bu kadar. Ancak bilgisayarlar karakterler hakkında hiçbir şey bilmezler; sadece baytları kullanabilirler. Bu nedenle, bir bilgisayar kullanarak metin depolamak veya iletmek istiyorsanız, karakterleri baytlara dönüştürmeniz gerekir. Bunu nasıl yaptın? İşte kodlamaların gerçekleştiği yer.

Kodlama, mantıksal karakterleri fiziksel baytlara çevirmenin bir kuralı dışında bir şey değildir. En basit ve en iyi bilinen kodlama ASCII'dir ve İngilizce yazıyorsanız ihtiyacınız olan tek şey budur. Diğer diller için, bugünlerde en güvenli seçim olan Unicode tatlarından herhangi biri olarak daha eksiksiz kodlamaya ihtiyacınız olacak.

Yani, kısacası, "kodlama kullanmadan bir dizginin baytlarını almak" için çalışmak, "herhangi bir dil kullanmadan bir metin yazmak" kadar mümkün değildir.

Bu arada, sizi (ve bu konuda kimseye) bu küçük bilgeliği okumanızı şiddetle tavsiye ediyorum: Mutlak Minimum Her Yazılım Geliştiricisi Kesinlikle, Olumlu Olarak Unicode ve Karakter Kümeleri Bilmeli (Mazeret Yok!)

21
Konamiman

C #, string bir byte dizisine dönüştürmek için:

public static byte[] StrToByteArray(string str)
{
   System.Text.UTF8Encoding  encoding=new System.Text.UTF8Encoding();
   return encoding.GetBytes(str);
}
20

Dize ve bayt dizisi arasında dönüşüm için aşağıdaki kodu kullanabilirsiniz.

string s = "Hello World";

// String to Byte[]

byte[] byte1 = System.Text.Encoding.Default.GetBytes(s);

// OR

byte[] byte2 = System.Text.ASCIIEncoding.Default.GetBytes(s);

// Byte[] to string

string str = System.Text.Encoding.UTF8.GetString(byte1);
16
Jarvis Stark
byte[] strToByteArray(string str)
{
    System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
    return enc.GetBytes(str);
}
15
gkrogers

Emin değilim, ancak dizginin bilgisini bayt ile verimsiz olan bir Karakter dizisi olarak sakladığını düşünüyorum. Spesifik olarak, Char tanımı "Unicode karakterini temsil eder" dir.

bu örnek örneği al:

String str = "asdf éß";
String str2 = "asdf gh";
EncodingInfo[] info =  Encoding.GetEncodings();
foreach (EncodingInfo enc in info)
{
    System.Console.WriteLine(enc.Name + " - " 
      + enc.GetEncoding().GetByteCount(str)
      + enc.GetEncoding().GetByteCount(str2));
}

Unicode cevabının her iki durumda da 14 bayt olduğuna dikkat edin, UTF-8 cevabı ise birincisi için sadece 9 byte, ikincisi için de sadece 7'dir.

Bu yüzden sadece dizenin kullandığı baytları istiyorsanız, sadece Encoding.Unicode kullanın, fakat depolama alanı ile verimsiz olacaktır.

12
Ed Marty

Span<T> 'nin C # 7.2 ile piyasaya sürülmesiyle, bir dizenin altında yatan bellek gösterimini yönetilen bir bayt dizisine yakalamak için kullanılan kanonik teknik şöyledir:

byte[] bytes = "rubbish_\u9999_string".AsSpan().AsBytes().ToArray();

Onu geri dönüştürmek, başlatıcı olmamalıdır, çünkü bu aslında verileri bir şekilde yorumladığınız anlamına gelir, ancak tamamlanma uğruna:

string s;
unsafe
{
    fixed (char* f = &bytes.AsSpan().NonPortableCast<byte, char>().DangerousGetPinnableReference())
    {
        s = new string(f);
    }
}

NonPortableCast ve DangerousGetPinnableReference adları, muhtemelen bunu yapmamanız gerektiği yönündeki argümanı içermelidir.

Span<T> ile çalışmanın System.Memory NuGet paketinin yüklenmesini gerektirdiğini unutmayın.

Ne olursa olsun, actual original soru ve takip yorumları, temel hafızanın "yorumlanmadığını" (ki bu, demek istediğim, olduğu gibi yazma ihtiyacının ötesinde değiştirilmediğini veya okunmadığını) belirtir Verileri tümüyle dizge olarak düşünmek yerine, Stream sınıfının bazı uygulamasının kullanılması gerektiği.

11
John Rasch

Önemli olan konu, bir dizgede bulunan bir glifin 32 bit (karakter kodu için 16 bit) almasıdır ancak bir baytın yalnızca 8 biti olması gerekir. Bire bir eşleme, yalnızca ASCII karakterini içeren dizelerle kısıtlanmadığınız sürece gerçekleştirilmez. System.Text.Encoding, bir dizgiyi bayt [] ile eşlemenin birçok yoluna sahiptir; bilgi kaybını önleyen ve bayt [] 'ı bir dizeye eşlemesi gerektiğinde müşteriniz tarafından kullanması kolay olanı seçmeniz gerekir. .

Utf8 popüler bir kodlamadır, kompakt ve kayıplı değildir.

10
Hans Passant

Kullanın:

    string text = "string";
    byte[] array = System.Text.Encoding.UTF8.GetBytes(text);

Sonuç:

[0] = 115
[1] = 116
[2] = 114
[3] = 105
[4] = 110
[5] = 103
8
mashet

Belirli bir kodlamayı manuel olarak belirtmeden .NET'te (C #) bir dizgiyi bir bayta [] nasıl dönüştürebilirim?

A string in .NET, bir UTF-16 kod birimi dizisi olarak metni temsil eder, bu nedenle baytlar zaten UTF-16'da bellekte kodlanmıştır.

Mehrdad'ın Cevabı

Mehrdad'ın cevabını kullanabilirsiniz, ancak karakter kodları UTF-16 olduğundan kodlama kullanıyor. source dizinine bakarak bir char[] oluşturan ve hafızayı doğrudan kopyalayan ToCharArray öğesini çağırır. Daha sonra verileri ayrıca tahsis edilen bir bayt dizisine kopyalar. Öyleyse, kaputun altında, altta yatan baytları iki kez kopyalamak ve çağrıdan sonra kullanılmayan bir karakter dizisi ayırmaktır.

Tom Blodget'in Cevabı

Tom Blodget'in cevabı , bir char dizisi ayırma ve baytları ona kopyalama işleminin orta aşamasını atladığından, Mehrdad'dan% 20-30 daha hızlıdır, ancak /unsafe seçeneğiyle derlemenizi gerektirir. Eğer kesinlikle kodlama kullanmak istemiyorsanız, bence bu yoldur. Şifreleme giriş bilgilerinizi fixed bloğunun içine koyarsanız, ayrı bir bayt dizisi ayırmanız ve baytları kopyalamanız gerekmez.

Ayrıca, kodlama neden dikkate alınmalıdır? Dize hangi baytların depolandığını basitçe öğrenemiyorum mu? Neden karakter kodlamasına bağımlılık var?

Çünkü bunu yapmanın doğru yolu budur. string bir soyutlamadır.

Bir kodlamanın kullanılması, geçersiz karakterli 'dizeler' varsa sorun çıkartabilir, ancak bu olmamalıdır. Geçersiz karakterlerle dizginize veri alıyorsanız, yanlış yapıyorsunuz demektir. Muhtemelen başlamak için bir bayt dizisi veya Base64 kodlaması kullanıyor olmalısınız.

System.Text.Encoding.Unicode kullanıyorsanız, kodunuz daha dayanıklı olacaktır. Kodunuzun üzerinde çalışacağı sistemin endianness 'ı için endişelenmenize gerek yok. CLR'nin bir sonraki sürümünün farklı bir dahili karakter kodlaması kullanması durumunda endişelenmenize gerek yok.

Bence soru neden kodlama konusunda endişelenmek istemediğinizi değil, neden görmezden gelip başka bir şey kullanmak istediğinizi düşünüyorum. Kodlamanın, bayt dizisindeki bir dizgenin soyutlamasını temsil etmesi amaçlanmıştır. System.Text.Encoding.Unicode size küçük bir endian bayt sırası kodlaması verecek ve şimdi ve gelecekte her sistemde aynı işlemi gerçekleştirecektir.

8
Jason Goemaat

En hızlı yol

public static byte[] GetBytes(string text)
{
    return System.Text.ASCIIEncoding.UTF8.GetBytes(text);
}

EDITMakotosan'ın yorumladığı gibi bu şimdi en iyi yol:

Encoding.UTF8.GetBytes(text)
8
Sunrising

OP'nin sorusuna en yakın yaklaşım, aslında nesneye giren ve baytları çıkaran Tom Blodget'inkidir. Ben en yakın diyorum çünkü bu String Nesnesinin uygulanmasına bağlı.

"Can't I simply get what bytes the string has been stored in?"

Tabii, ama sorudaki temel hatanın ortaya çıktığı yer burasıdır. Dize, ilginç bir veri yapısına sahip olabilecek bir nesnedir. Bunu zaten biliyoruz, çünkü eşleştirilmemiş taşıyıcıların saklanmasına izin veriyor. Uzunluğu saklayabilir. Hızlı sayma imkanı sağlayan 'eşleştirilmiş' vekillerin her birine işaret edebilir. Vb Bu ekstra baytların tümü karakter verilerinin bir parçası değildir.

İstediğiniz şey, bir dizideki her karakterin baytıdır. Ve 'kodlamanın' geldiği yer burasıdır. Varsayılan olarak UTF-16LE'yi alırsınız. Gidiş-dönüş hariç baytların kendileri umrunda değilse, 'varsayılan' da dahil olmak üzere herhangi bir kodlamayı seçebilir ve daha sonra geri dönüştürebilirsiniz (varsayılan kodlamanın ne olduğu gibi aynı parametreleri varsayarak, kod noktaları, hata düzeltmeleri) eşleşmemiş suretler vb. gibi şeylere izin verilir.

Ama neden 'kodlamayı' büyüye bırakıyorsun? Niye alacağınızı bilmek için neden kodlamayı belirtmiyorsunuz?

"Why is there a dependency on character encodings?"

Kodlama (bu bağlamda) sadece dizenizi temsil eden bayt anlamına gelir. String nesnesinin baytları değil. Dizenin içinde bulunduğu baytları istediniz - bu, sorunun saf olarak sorulduğu yerdi. Bir dize nesnesinin içerebileceği diğer ikili verilerin tümünü değil, dizeyi temsil eden bitişik bir dizide dizenin bayt olmasını istediniz.

Bu, bir dizenin nasıl saklandığı anlamına gelmez. Bir bayt dizisindeki baytlara "Kodlanmış" bir dize istiyorsunuz.

Tom Bloget'in cevabını beğendim çünkü sizi 'string nesnesinin bayt' yönüne götürdü. Yine de uygulamaya bağlı ve iç organlara göz attığı için dizenin bir kopyasını yeniden oluşturmak zor olabilir.

Mehdad'ın tepkisi yanlıştır çünkü kavramsal düzeyde yanıltıcıdır. Hala kodlanmış bir bayt listeniz var. Özel çözümü, eşleştirilmemiş taşıyıcıların korunmasını sağlar - bu uygulamaya bağlıdır. Özel çözümü, GetBytes dizeyi varsayılan olarak UTF-8'de döndürdüyse dizenin baytlarını doğru şekilde üretmez.


Bu konuda fikrimi değiştirdim (Mehrdad'ın çözümü) - bu dizginin baytını almıyor; bunun yerine, dizeden yaratılan karakter dizisinin baytlarını alıyor. Kodlamaya bakılmaksızın, c # içindeki char veri türü sabit bir boyuttur. Bu, tutarlı bir uzunluk bayt dizisinin üretilmesine izin verir ve karakter dizisinin bayt dizisinin boyutuna bağlı olarak çoğaltılmasını sağlar. Yani kodlama UTF-8 olsaydı, ancak her karakter en büyük utf8 değerine uyması için 6 bayt olsaydı, yine de çalışırdı. Yani gerçekten - karakterin kodlanması önemli değil.

Fakat bir dönüşüm kullanıldı - her karakter sabit büyüklükte bir kutuya yerleştirildi (c # karakter tipi). Bununla birlikte, bu temsilin önemi yoktur, teknik olarak OP'nin cevabı budur. Yani - yine de dönüştürecekseniz ... Neden 'kodlamıyorsunuz'?

6
Gerard ONeill

.NET'te string bir byte array dönüştürmek için aşağıdaki kodu kullanabilirsiniz

string s_unicode = "abcéabc";
byte[] utf8Bytes = System.Text.Encoding.UTF8.GetBytes(s_unicode);
6

İşte güvenli olmayan String - Byte[] dönüşümü uygulaması:

public static unsafe Byte[] GetBytes(String s)
{
    Int32 length = s.Length * sizeof(Char);
    Byte[] bytes = new Byte[length];

    fixed (Char* pInput = s)
    fixed (Byte* pBytes = bytes)
    {
        Byte* source = (Byte*)pInput;
        Byte* destination = pBytes;

        if (length >= 16)
        {
            do
            {
                *((Int64*)destination) = *((Int64*)source);
                *((Int64*)(destination + 8)) = *((Int64*)(source + 8));

                source += 16;
                destination += 16;
            }
            while ((length -= 16) >= 16);
        }

        if (length > 0)
        {
            if ((length & 8) != 0)
            {
                *((Int64*)destination) = *((Int64*)source);

                source += 8;
                destination += 8;
            }

            if ((length & 4) != 0)
            {
                *((Int32*)destination) = *((Int32*)source);

                source += 4;
                destination += 4;
            }

            if ((length & 2) != 0)
            {
                *((Int16*)destination) = *((Int16*)source);

                source += 2;
                destination += 2;
            }

            if ((length & 1) != 0)
            {
                ++source;
                ++destination;

                destination[0] = source[0];
            }
        }
    }

    return bytes;
}

Olduğu kadar zarif olmasa bile, kabul görmüş olanınkinden çok daha hızlı. İşte 10000000'in üzerinde yineleme kronometre kriterlerim:

[Second String: Length 20]
Buffer.BlockCopy: 746ms
Unsafe: 557ms

[Second String: Length 50]
Buffer.BlockCopy: 861ms
Unsafe: 753ms

[Third String: Length 100]
Buffer.BlockCopy: 1250ms
Unsafe: 1063ms

Kullanmak için, proje oluşturma özelliklerinde "Güvensiz Kodlara İzin Ver" i işaretlemelisiniz. .NET Framework 3.5'e göre, bu yöntem String uzantısı olarak da kullanılabilir:

public static unsafe class StringExtensions
{
    public static Byte[] ToByteArray(this String s)
    {
        // Method Code
    }
}
4
Tommaso Belluzzo

Bir dizginin altındaki baytlarının gerçekten bir kopyasını istiyorsanız, izleyen gibi bir işlevi kullanabilirsiniz. Bununla birlikte, yapmamalısınız lütfen nedenini bulmak için okumaya devam edin.

[DllImport(
        "msvcrt.dll",
        EntryPoint = "memcpy",
        CallingConvention = CallingConvention.Cdecl,
        SetLastError = false)]
private static extern unsafe void* UnsafeMemoryCopy(
    void* destination,
    void* source,
    uint count);

public static byte[] GetUnderlyingBytes(string source)
{
    var length = source.Length * sizeof(char);
    var result = new byte[length];
    unsafe
    {
        fixed (char* firstSourceChar = source)
        fixed (byte* firstDestination = result)
        {
            var firstSource = (byte*)firstSourceChar;
            UnsafeMemoryCopy(
                firstDestination,
                firstSource,
                (uint)length);
        }
    }

    return result;
}

Bu işlev, dizenizin altındaki baytların bir kopyasını oldukça çabucak alır. Bu baytları, sisteminizde hangi şekilde kodladıklarını alırsınız. Bu kodlama neredeyse kesinlikle UTF-16LE'dir ancak bu, dikkat etmeniz gerekmeyen bir uygulama detayıdır.

Daha güvenli, daha basit ve daha güvenilir olacaktır Sadece aramak için,

System.Text.Encoding.Unicode.GetBytes()

Herhalde bu aynı sonucu verecek, yazması daha kolay ve baytlar her zaman bir çağrı ile bir araya gelecek

System.Text.Encoding.Unicode.GetString()
3
Jodrell

Basitçe şunu kullanın:

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

Dize, aşağıdaki gerçeklerden dolayı, birkaç farklı yolla bayt dizisine dönüştürülebilir: .NET, Unicode'u destekler ve Unicode, UTF olarak adlandırılan birkaç fark kodlamasını standartlaştırır. Farklı uzunluklarda bayt temsilleri vardır ancak bu, bir dize kodlandığında, dizeye geri kodlanabileceği anlamına gelir, ancak dize bir UTF ile kodlanmışsa ve vidalanabiliyorsa, farklı UTF varsayımına göre kodlanmışsa yukarı.

Ayrıca, .NET Unicode olmayan kodlamaları da destekler, ancak bunlar genel olarak geçerli değildir (yalnızca ASCII gibi gerçek bir dizgide sınırlı bir Unicode kod noktası alt kümesi kullanıldığında geçerli olacaktır). Dahili olarak, .NET UTF-16'yı destekler, ancak akış gösterimi için UTF-8 genellikle kullanılır. Aynı zamanda internet için standart bir yöntemdir.

Şaşırtıcı olmayan bir şekilde, dizginin bir bayt dizisine seri hale getirilmesi ve seriyi kaldırma, soyut bir sınıf olan System.Text.Encoding sınıfı tarafından desteklenir; türetilmiş sınıfları somut kodlamaları destekler: ASCIIEncoding ve dört UTF (System.Text.UnicodeEncoding UTF-16'yı destekler)

Ref bu bağlantı.

System.Text.Encoding.GetBytes kullanarak bayt dizisine serileştirme için. Ters işlem için System.Text.Encoding.GetChars kullanın. Bu işlev bir karakter dizisi döndürür, bir dize elde etmek için, bir string constructor System.String(char[]) kullanın.
Bu sayfayı ref.

Örnek:

string myString = //... some string

System.Text.Encoding encoding = System.Text.Encoding.UTF8; //or some other, but prefer some UTF is Unicode is used
byte[] bytes = encoding.GetBytes(myString);

//next lines are written in response to a follow-up questions:

myString = new string(encoding.GetChars(bytes));
byte[] bytes = encoding.GetBytes(myString);
myString = new string(encoding.GetChars(bytes));
byte[] bytes = encoding.GetBytes(myString);

//how many times shall I repeat it to show there is a round-trip? :-)
2
Vijay Singh Rana

Bu baytların ne istediğine bağlıdır

Bunun nedeni, Tyler’ın çok uygun bir şekilde dedi , "Dizeler saf veri değil. Ayrıca bilgi ." Bu durumda, bilgi dize oluşturulduğunda varsayılan bir kodlamadır.

Bir dizede saklanan ikili verilerinizin (metin yerine) olduğunu varsayarsak

Bu OP'nin kendi sorusundaki yorumuna dayanıyor ve OP'nin kullanımdaki ipuçlarını anlıyorsam doğru soru.

İkili veriyi dizgelerde saklamak muhtemelen yukarıda bahsedilen kodlamanın yanlış olması nedeniyle yanlış bir yaklaşımdır! Bu ikili verileri string içinde (daha uygun olan bir byte[] dizisi yerine) depolanan herhangi bir program veya kütüphane, başlamadan önce zaten savaşı kaybetti. Baytları size REST istek/yanıtında veya - dizeleri iletecek herhangi bir şeyde gönderiyorlarsa, Base64 doğru bir yaklaşım olacaktır.

Bilinmeyen bir kodlamaya sahip bir metin dizeniz varsa

Diğer herkes bu yanlış soruyu yanlış cevapladı.

Dize olduğu gibi iyi görünüyorsa, sadece bir kodlama seçin (tercihen UTF ile başlayan), karşılık gelen System.Text.Encoding.???.GetBytes() işlevini kullanın ve seçtiğiniz şifreyi baytlara kime verdiğinizi söyleyin.

2
NH.

LINQ ile basit kod

string s = "abc"
byte[] b = s.Select(e => (byte)e).ToArray();

EDIT: aşağıda yorumlandığı gibi, iyi bir yol değil.

ancak daha uygun bir kodlamayla LINQ'u anlamak için hala kullanabilirsiniz:

string s = "abc"
byte[] b = s.Cast<byte>().ToArray();
2
Avlin

İki yol:

public static byte[] StrToByteArray(this string s)
{
    List<byte> value = new List<byte>();
    foreach (char c in s.ToCharArray())
        value.Add(c.ToByte());
    return value.ToArray();
}

Ve,

public static byte[] StrToByteArray(this string s)
{
    s = s.Replace(" ", string.Empty);
    byte[] buffer = new byte[s.Length / 2];
    for (int i = 0; i < s.Length; i += 2)
        buffer[i / 2] = (byte)Convert.ToByte(s.Substring(i, 2), 16);
    return buffer;
}

En alttan bir tanesini daha sık kullanmaya meyilliyim, hız için kıyaslama yapmadım.

2
harmonik
bytes[] buffer = UnicodeEncoding.UTF8.GetBytes(string something); //for converting to UTF then get its bytes

bytes[] buffer = ASCIIEncoding.ASCII.GetBytes(string something); //for converting to ascii then get its bytes
2
user1120193

Bir karakter, hem bir yazı tipi tablosunda arama anahtarı hem de sipariş verme, büyük ve küçük harf sürümleri vb.

Sonuç olarak, bir karakter bir bayt değildir (8-bit) ve bir bayt bir karakter değildir. Özellikle, bir baytın 256 permütasyonu bazı yazılı diller içindeki binlerce sembolü barındıramaz, tüm dillerden çok daha az. Bu nedenle, karakterleri kodlamak için çeşitli yöntemler geliştirilmiştir. Bazıları belirli bir dil sınıfını kodlar (ASCII kodlaması); kod sayfalarını kullanan çoklu diller (Genişletilmiş ASCII); veya, iddialı bir şekilde, gerektiğinde ek baytlar da dahil olmak üzere seçici olarak tüm dilleri, Unicode.

.NET çerçevesi gibi bir sistem içinde, bir String belirli bir karakter kodlamasını ifade eder. .NET'te bu kodlama Unicode'dur. Çerçeve varsayılan olarak Unicode okuyup yazdığından, karakter kodlaması ile uğraşmak genellikle .NET'te gerekli değildir.

Bununla birlikte, genel olarak, bir karakter dizgisini sisteme bir bayt akışından yüklemek için, kaynak kodlamasını bilmeniz gerekir, bu nedenle doğru bir şekilde yorumlayın ve tercüme edin (aksi halde kodlar sistemin varsayılan kodlamasında olduğu gibi alınacaktır ve anlamsız). Benzer şekilde, bir dize harici bir kaynağa yazıldığında, belirli bir kodlamaya yazılır.

0
George

byte[]'dan string'a:

        return BitConverter.ToString(bytes);
0
Piero Alberto

Bir dizgiyi bir bayta [] dönüştürmek için aşağıdaki çözümü kullanın:

string s = "abcdefghijklmnopqrstuvwxyz";
byte[] b = System.Text.UTF32Encoding.GetBytes(s);

Umut ediyorum bu yardım eder.

0

Kabul edilen cevaba benzer bir Visual Basic uzantısı yazdım, ancak doğrudan .NET belleği ve dönüştürme için Marshalling'i kullandım ve diğer yöntemlerde, UnicodeEncoding.UTF8.GetString veya UnicodeEncoding.UTF32.GetString veya hatta MemoryStream and BinaryFormatter gibi desteklenmeyen karakter aralıklarını destekler (geçersiz karakterler gibi: ???? & ChrW(55906) & ChrW(55655)):

<Extension> _
Public Function ToBytesMarshal(ByRef str As String) As Byte()
    Dim gch As GCHandle = GCHandle.Alloc(str, GCHandleType.Pinned)
    Dim handle As IntPtr = gch.AddrOfPinnedObject
    ToBytesMarshal = New Byte(str.Length * 2 - 1) {}
    Try
        For i As Integer = 0 To ToBytesMarshal.Length - 1
            ToBytesMarshal.SetValue(Marshal.ReadByte(IntPtr.Add(handle, i)), i)
        Next
    Finally
        gch.Free()
    End Try
End Function

<Extension> _
Public Function ToStringMarshal(ByRef arr As Byte()) As String
    Dim gch As GCHandle = GCHandle.Alloc(arr, GCHandleType.Pinned)
    Try
        ToStringMarshal = Marshal.PtrToStringAuto(gch.AddrOfPinnedObject)
    Finally
        gch.Free()
    End Try
End Function
0
Mojtaba Rezaeian