Manuel olarak bir kodlama belirtmeden C # dizeleri tutarlı bir bayt temsili nasıl alabilirim?


2189

Nasıl dönüştürebilirim stringa byte[]el ile belirli kodlama belirtmeden .NET (C #) içinde?

Dizeyi şifreleyeceğim. Dönüştürmeden şifreleyebilirim, ama yine de kodlamanın neden burada oynamaya geldiğini bilmek istiyorum.

Ayrıca, kodlama neden dikkate alınmalıdır? Dize içinde saklanan baytları alamıyor muyum? Neden karakter kodlamalarına bağımlılık var?


23
Her dize bir bayt dizisi olarak saklanır değil mi? Neden bu baytlara sahip olamıyorum?
Agnel Kurian

135
Kodlama , karakterleri baytlarla eşleştiren şeydir. Örneğin, ASCII'de 'A' harfi 65 rakamıyla eşleşir. Farklı bir kodlamada aynı olmayabilir. .NET çerçevesinde alınan dizelere yönelik üst düzey yaklaşım, bunu (bu durum hariç) büyük ölçüde alakasız hale getirir.
Lucas Jones

20
Şeytanın avukatını oynamak için: Bir bellek içi dizenin baytlarını almak istiyorsanız (.NET'in kullandığı gibi) ve bunları bir şekilde manipüle etmek (yani CRC32) ve ASLA ASLA onu orijinal dizgiye geri kodlamak istemedi ... neden kodlamaları önemsediğinizi veya hangisini kullanacağınızı nasıl seçeceğinizi açıklamak kolay değildir.
Greg

78
Kimse bu bağlantıyı henüz şaşırtmadı
Bevan

28
Bir karakter bayt değildir ve bir bayt karakter değildir. Karakter, hem yazı tipi tablosunun anahtarı hem de sözcüksel bir gelenek. Dize karakter dizisidir. (Bir kelime, paragraf, cümle ve başlık da kendi tür tanımlarını haklı çıkaran kendi sözcüksel geleneklerine sahiptir - ama ben araştırıyorum). Tamsayılar, kayan nokta sayıları ve diğer her şey gibi, karakterler de bayt olarak kodlanır. Kodlamanın basit bire bir olduğu bir zaman vardı: ASCII. Bununla birlikte, tüm insan sembolojisini barındırmak için, bir baytın 256 permütasyonu yetersizdi ve kodlamalar seçici olarak daha fazla bayt kullanacak şekilde tasarlandı.
George

Yanıtlar:


1855

Burada cevapları aksine, kodlama hakkında endişe gerek YAPMAYIN eğer bayt yorumlanabilir gerek yok!

Bahsettiğiniz gibi, amacınız basitçe "dizginin içinde saklandığı baytları almak" tır .
(Ve elbette, dizeyi baytlardan yeniden oluşturabilmek için.)

Bu hedefler için dürüst do not insanlar kodlamaları gerektiğini belirten tutmak anlamak. Bunun için kodlamalar konusunda endişelenmenize gerek yoktur.

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

// Do NOT use on arbitrary bytes; only use on GetBytes's output on the SAME system
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 baytları yorumlamaya çalışmadığı sürece, bunu yapmak istediğinizden bahsetmediğiniz açıktır, o zaman bu yaklaşımda yanlış bir şey yoktur ! Kodlamalardan endişe etmek, gerçek bir sebep olmadan hayatınızı daha karmaşık hale getirir.

Bu yaklaşıma ek fayda:

Dizenin geçersiz karakterler içerip içermemesi önemli değildir, çünkü yine de verileri alıp orijinal dizgiyi yeniden yapılandırabilirsiniz!

Aynı şekilde kodlanacak ve deşifre edilecektir, çünkü sadece baytlara bakıyorsunuz .

Bununla birlikte, belirli bir kodlama kullandıysanız, geçersiz karakterleri kodlama / kod çözme konusunda sorun yaşarsınız.


247
Ne çirkin bu biridir hakkında böyle GetStringve GetBytesişe aynı endian sahip bir sistem üzerinde yürütülen ihtiyacı. Bu nedenle, başka bir yerde bir dizeye dönüştürmek istediğiniz bayt almak için bunu kullanamazsınız. Bu yüzden bunu kullanmak isteyeceğim bir durum bulmakta zorlanıyorum.
CodesInChaos

72
@CodeInChaos: Söylediğim gibi, tüm mesele, onu aynı işlevler dizisiyle aynı tür bir sistemde kullanmak istiyorsanız. Değilse, kullanmamalısınız.
user541686

193
-1 Birisinin (baytlara karşı karakterleri anlamayan) dizelerini bir bayt dizisine dönüştürmek isteyeceğini, google'ı okuyacak ve bu cevabı okuyacağını ve yanlış şeyi yapacaklarını, çünkü neredeyse tümünde durumlarda, kodlama IS alakalı.
artbristol

401
@artbristol: Cevabı (veya diğer cevapları ...) okumaktan rahatsız olmazlarsa, üzgünüm, onlarla iletişim kurmamın daha iyi bir yolu yok. Genellikle cevabımla başkalarının neler yapabileceğini tahmin etmeye çalışmak yerine OP'ye cevap vermeyi tercih ederim - OP'nin bilme hakkı vardır ve birisinin bir bıçağı kötüye kullanabilmesi dünyadaki tüm bıçakları gizlememiz gerektiği anlamına gelmez kendimiz için. Buna katılmıyorsanız, bu da iyidir.
user541686

185
Bu cevap pek çok düzeyde yanlıştır, ancak en önemlisi "kodlama konusunda endişelenmenize gerek yok!" GetBytes ve GetString gibi iki yöntem, Encoding.Unicode.GetBytes () ve Encoding.Unicode.GetString () öğelerinin zaten yaptıkları şeylerin yeniden uygulamaları kadar gereksizdir. "Programınız (veya diğer programlar) baytları yorumlamaya çalışmadığı sürece" ifadesi de, baytların Unicode olarak yorumlanması gerektiği anlamına geldiği için temelde kusurludur.
David

1108

Dizenizin 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ğuna dair 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 kullanır , bu nedenle .NET'in kullandığı tam baytları almak istiyorsanız kullanın System.Text.Encoding.Unicode.GetBytes (...).

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


14
Ancak, kodlama neden dikkate alınmalıdır? Hangi kodlamanın kullanıldığını görmek zorunda kalmadan neden baytları alamıyorum? Gerekli olsa bile, String nesnesinin kendisi hangi kodlamanın kullanıldığını bilmemeli ve sadece bellekte ne olduğunu dökmemeli mi?
Agnel Kurian

57
Bir .NET dizeleri her zaman Unicode olarak kodlanır. Yani System.Text.Encoding.Unicode.GetBytes (); .NET'in karakterleri temsil etmek için kullandığı bayt kümesini almak için. Ama neden bunu istiyorsun? UTF-8'i özellikle çoğu karakter batı latin setindeyken öneririm.
AnthonyWJones

8
Ayrıca: dizede dahili olarak kullanılan tam bayt önemi yok verilerini geri çağırır onları yanlış kodlama olarak o kodlamayı veya kolları idare etmediğini eğer sistem. Eğer hepsi .Net içindeyse, neden bir bayt dizisine dönüştürebilirsiniz? Aksi takdirde, kodlamanızla açık olmak daha iyidir
Joel Coehoorn

11
@Joel, Çalıştığı her makinede farklı olabileceğinden, System.Text.Encoding.Default'a dikkat edin. Bu nedenle her zaman UTF-8 gibi bir kodlama belirtmeniz önerilir.
Ash

25
Siz (veya bir başkası) verileri genel bir "bayt bloğu" olarak işlemek yerine yorumlamak istemediğiniz sürece kodlamaya ihtiyacınız yoktur . Sıkıştırma, şifreleme gibi şeyler için kodlamadan endişe etmek anlamsızdır. Kodlama konusunda endişelenmeden bunu yapmanın bir yolu için cevabımı görün . (
Yapmadığınız

285

Kabul edilen cevap çok ama ç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);

Gerekmiyorsa tekerleği yeniden icat etmeyin ...


14
Kabul edilen cevabın değişmesi durumunda, kayıt amacıyla, bu şimdiki zaman ve tarihte Mehrdad'ın cevabıdır. Umarım OP bunu tekrar ziyaret eder ve daha iyi bir çözümü kabul eder.
Thomas Eding

7
prensipte iyi fakat kodlama System.Text.Encoding.UnicodeMehrdad'ın cevabına eşdeğer olmalıdır .
Jodrell

5
Soru, orijinal yanıttan bu yana bir umptillion kez düzenlendi, bu yüzden belki de cevabım biraz daha fazla. Hiç Mehrdad'ın cevabına eşdeğer bir övgü vermek istemedim, ama bunu yapmanın mantıklı bir yolunu verdim. Ama haklı olabilirsin. Bununla birlikte, orijinal sorudaki "dizenin hangi baytta saklandığını al" ifadesi çok kesin değildir. Nerede saklanır? Bellekte? Diskte mi? Eğer hafızada System.Text.Encoding.Unicode.GetBytesolsaydı, muhtemelen daha kesin olurdu.
Erik A. Brandstadmoen

7
@AMissico, dizenizin sistem varsayılan kodlamanızla uyumlu olmadığından (sistem varsayılan eski karakter kümenizde yalnızca ASCII karakterleri içeren dize) emin olmadığınız sürece öneriniz buggy'dir. Ama OP hiçbir yerde bunu belirtmiyor.
Frédéric

5
@AMissico Programın farklı sistemlerde farklı sonuçlar vermesine neden olabilir . Bu asla iyi bir şey değil. Bir karma veya bir şey yapmak için olsa bile (OP'nin 'şifrelemek' ile ne anlama geldiğini varsayıyorum), aynı dize her zaman aynı hash'i vermelidir.
Nyerguds

114
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());

2
Tüm bu işlemler için aynı BinaryFormatter örneğini kullanabilirsiniz
Joel Coehoorn

3
Çok ilginç. Görünüşe göre yüksek vekil Unicode karakteri düşecek. [BinaryFormatter ] ile ilgili belgelere bakın

95

Kodlamayı dikkate almanız gerekir, çünkü 1 karakter 1 veya daha fazla bayt (yaklaşık 6'ya kadar) ile temsil edilebilir ve farklı kodlamalar bu baytlara farklı şekilde davranır.

Joel'in bu konuda bir yazısı var:

Mutlak Minimum Her Yazılım Geliştiricisi Kesinlikle, Olumlu Unicode ve Karakter Kümeleri Hakkında Bilmeli (Bahane Yok!)


6
"1 karakter 1 veya daha fazla bayt ile temsil edilebilir" diye katılıyorum. Ben sadece ne dize kodlama olursa olsun bu bayt istiyorum. Bir dize bellekte saklanabilir tek yolu bayt cinsindendir. Karakterler bile 1 veya daha fazla bayt olarak saklanır. Sadece ellerimi baytlara almak istiyorum.
Agnel Kurian

16
Siz (veya bir başkası) verileri genel bir "bayt bloğu" olarak işlemek yerine yorumlamak istemediğiniz sürece kodlamaya ihtiyacınız yoktur . Sıkıştırma, şifreleme gibi şeyler için kodlamadan endişe etmek anlamsızdır. Kodlama konusunda endişelenmeden bunu yapmanın bir yolu için cevabımı görün .
user541686

9
@Mehrdad - Tamamen, ama başlangıçta cevap verdiğim gibi orijinal soru, OP'nin bu baytlarla dönüştürüldükten sonra ne olacağını bilmiyordu ve gelecekteki araştırmacılar için ilgili bilgiler - bu Joel'in cevabının oldukça güzel bir şekilde kapsanması - ve cevabınızın içinde belirttiğiniz gibi: .NET dünyasına bağlı kalmanız ve yöntemlerinizi dönüştürmek / almak için yöntemlerinizi kullanmanız koşuluyla mutlu olursunuz. Bunun dışına çıktığınızda kodlama önemli olacaktır.
Zhaph - Ben Duguid

Bir kod noktası 4 bayta kadar temsil edilebilir . (Bir UTF-32 kod birimi, UTF-16 yedek çifti veya 4 bayt UTF-8.) UTF-8'in 4 bayttan daha fazlasına ihtiyaç duyacağı değerler Unicode'un 0x0..0x10FFFF aralığının dışında. ;-)
DevSolar

89

Bu popüler bir soru. Yazarın ne sorduğunu ve bunun en yaygın ihtiyaç olandan farklı olduğunu anlamak önemlidir. Kodun gerekli olmadığı yerlerde kötüye kullanılmasını engellemek için, daha sonra önce cevap verdim.

Ortak İhtiyaç

Her dizenin bir karakter kümesi ve kodlaması vardır. Bir System.Stringnesneyi bir diziye dönüştürdüğünüzde System.Bytehala bir karakter kümesi ve kodlaması vardır. Çoğu kullanım için, hangi karakter kümesine ve kodlamaya ihtiyacınız olduğunu bilirsiniz ve .NET "dönüşümle kopyalama" yı kolaylaştırır. Sadece uygun Encodingsınıfı seçin .

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

Dönüşümün, hedef karakter kümesinin veya kodlamanın kaynaktaki bir karakteri desteklemediği durumları ele alması gerekebilir. Bazı seçenekleriniz var: istisna, değiştirme veya atlama. Varsayılan ilke '?' Yerine kullanılı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.StringKaynak karakter kümesi Unicode'dur.

Kafa karıştırıcı olan tek ş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çağrılmalıdır Encoding.UTF16.

Çoğu kullanım için bu kadar. Eğer ihtiyacınız olan buysa, burada okumayı bırakın. Bir kodlamanın ne olduğunu anlamadıysanız eğlenceli Joel Spolsky makalesine bakın .

Özel İhtiyaç

Şimdi, soru yazar, "Her dize bir bayt dizisi olarak saklanır, değil mi? Neden bu baytlara sahip olamıyorum?"

Herhangi bir dönüşüm istemiyor.

Gönderen C # spec :

C # 'da karakter ve dize işleme Unicode kodlaması kullanır. Karakter türü bir UTF-16 kod birimini ve dize türü bir dizi UTF-16 kod birimini temsil eder.

Dolayısıyla, boş dönüşüm (yani UTF-16'dan UTF-16'ya) istersek, istenen sonucu alacağımızı biliyoruz:

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

Ancak kodlamalardan bahsetmemek için, bunu başka bir şekilde yapmalıyız. Bir ara veri türü kabul edilebilirse, bunun için kavramsal bir kısayol vardır:

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

Bu bize istenen veri tipini vermez, ancak Mehrdad'ın cevabı , bu Char dizisinin BlockCopy kullanarak bir Byte dizisine nasıl dönüştürüleceğini gösterir . Ancak, bu dizeyi iki kez kopyalar! Ve çok açık bir şekilde kodlamaya özgü kod kullanır: veri türü System.Char.

Dize'nin saklandığı gerçek baytlara ulaşmanın tek yolu bir işaretçi kullanmaktır. fixedİfadesi değerlerinin adresini alarak verir. C # spec'ten:

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

Bunu yapmak için, derleyici kod atlama ile dize nesnesinin diğer bölümleri üzerine yazar RuntimeHelpers.OffsetToStringData. Ham baytları almak için 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 endianitesine bağlıdır. Ancak soru yazarı bununla ilgilenmiyor.


3
@Jan Doğru ama dize uzunluğu zaten kod birimi sayısını veriyor (kod noktası değil).
Tom Blodget

1
Bunu işaret ettiğiniz için teşekkürler! MSDN'den: " Length[of String] özelliği Char, Unicode karakter sayısını değil, bu örnekteki nesne sayısını döndürür ." Bu nedenle örnek kodunuz yazıldığı gibi doğrudur.
Jan Hettich

1
@supercat "Karakter türü UTF-16 kod birimini, dize türü de UTF-16 kod birimini temsil eder." —_ C # 5 Özellikler._ Evet, ancak geçersiz bir Unicode dizesini engelleyen hiçbir şey yoktur:new String(new []{'\uD800', '\u0030'})
Tom Blodget

1
@TomBlodget: Bir örneklerini alır İlginçtir, Globalization.SortKeyözü KeyDatabir içine her birinden ve paketlerini elde edilen bayt String[karakter iki bayt, MSB ilk ], çağrı String.CompareOrdinalelde edilen dizeleri çağrıda önemli ölçüde daha hızlı olacaktır SortKey.Compareolaylarına SortKeyveya memcmpbu örnekleri bile çağırıyor . Bu göz önüne alındığında, merak ediyorum neden KeyDatabir Byte[]yerine bir geri döner String?
supercat

1
Ne yazık ki, doğru cevap, ancak yıllar çok geç, asla kabul edilen kadar oy almayacak. TL nedeniyle; DR insanları kabul edilen cevapların düştüğünü düşünecek. kopyalayın ve oy verin.
Martin Capodici

46

Sorunuzun ilk kısmı (baytların nasıl elde edileceği) başkaları tarafından zaten cevaplandı: System.Text.Encodingad alanına bakın.

Takip eden sorunuza değineceğim: neden bir kodlama seçmeniz gerekiyor? Bunu neden string sınıfının kendisinden alamıyorsunuz?

Cevap iki bölümden oluşmaktadır.

Her şeyden önce, string sınıfı tarafından dahili olarak kullanılan baytlar önemli değildir ve ne zaman yaparsanız yapın muhtemelen bir hata getiriyorsunuzdur.

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, verileri iletme konusunda endişelenmek için .Net Serialization kullanın. Artık gerçek baytlar için endişelenmenize gerek yok: Serileştirme formatlayıcı bunu sizin için yapar.

Öte yandan, bu baytları bir .Net serileştirilmiş akışından veri çekeceğini garanti edemeyeceğiniz bir yere gönderiyorsanız? Bu durumda kesinlikle kodlama konusunda endişelenmeniz gerekir, çünkü bu harici sistem umurundadır. Bu nedenle, dize tarafından kullanılan dahili baytlar önemli değildir: bir kodlama seçmeniz gerekir, böylece .Net tarafından dahili olarak kullanılan kodlama ile aynı olsa bile, alıcı uçtaki bu kodlama hakkında açık olursunuz.

Bu durumda, bellekte dize değişkeni tarafından saklanan gerçek baytları mümkünse, bayt akışınızı oluşturmak için biraz çalışma kaydedebileceği fikri ile kullanmayı tercih edebileceğinizi anlıyorum. Ancak, bu emin çıkış diğer ucunda anlaşılmaktadır hale kıyasla sadece önemli değil, ve o garantiye size koymak gerekir kodlamayla ilgili açık ve net olması. Ayrıca, dahili baytlarınızla gerçekten eşleşmek istiyorsanız, zaten Unicodekodlamayı seçebilir ve bu performans tasarruflarını elde edebilirsiniz.

Hangi toplama ... İkinci bölümde getiriyor Unicodekodlamayı edilir yatan byte kullanarak .NET anlatan. Bu kodlamayı seçmeniz gerekiyor, çünkü bazı yeni fangled Unicode-Plus ortaya çıktığında .Net çalışma zamanının bu yeni, daha iyi kodlama modelini programınızı bozmadan kullanmak için ücretsiz olması gerekir. Ancak, şimdilik (ve öngörülebilir bir gelecek için), sadece Unicode kodlamasını seçmek size ne istediğinizi verir.

Dizenizin kabloya yeniden yazılması gerektiğini anlamak da önemlidir ve bu, eşleşen bir kodlama kullansanız bile bit deseninin en azından bazı çevirilerini içerir . Bilgisayarın Big vs Little Endian, ağ bayt sırası, paketleme, oturum bilgileri vb.


9
.NET'te dizeler için bayt dizileri almanız gereken alanlar vardır. .NET Cryptrography sınıflarının çoğu, bayt dizisini veya akışını kabul eden ComputeHash () gibi yöntemler içerir. Bir dizeyi önce bir bayt dizisine (Kodlama seçerek) dönüştürmek ve sonra isteğe bağlı olarak bir akışa sarmaktan başka alternatifiniz yoktur. Bununla birlikte, bir kodlama (yani UTF8) bir çubuk seçtiğiniz sürece bununla ilgili bir sorun yoktur.
Kül

44

Sadece Mehrdrad ses göstermek için cevap eserler, onun yaklaşımı bile devam edebilir eşleşmemiş yedek karakterlerini birçok cevabım hakkında öne sürdüğü olan (fakat bunlardan herkes örneğin aynı derecede suçludur System.Text.Encoding.UTF8.GetBytes, System.Text.Encoding.Unicode.GetBytes; bu kodlama yöntemleri yüksek suret devam edemez karakterler d800, örneğin ve bu sadece sadece değerle yüksek vekil karakterleri değiştirmek fffd):

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

Bunu System.Text.Encoding.UTF8.GetBytes veya System.Text.Encoding.Unicode.GetBytes ile deneyin , yalnızca yüksek vekil karakterleri fffd değeriyle değiştirecekler

Bu soruda her hareket olduğunda, hala eşleşmemiş vekil karakterleri içeriyor olsa bile dizeleri devam ettirebilecek bir serileştirici (Microsoft veya 3. taraf bileşeninden olsun) düşünüyorum; Ben her zaman bu google: serileştirme eşleşmemiş vekil karakter. NET . Bu beni hiç uykuyu kaybettirmiyor, ama şimdi ve sonra cevabımın kusurlu olduğuna dair yorum yapan birileri rahatsız edici, ama eşleşmemiş vekil karakterler söz konusu olduğunda cevapları eşit derecede kusurlu.

Lanet, Microsoft sadece kullanılmış olmalıdır System.Buffer.BlockCopyonun içinde BinaryFormatter

谢谢!


3
Taşıyıcıların geçerli kod noktaları oluşturmak için çiftler halinde görünmesi gerekmez mi? Eğer durum buysa, verinin neden karışacağını anlayabiliyorum.
dtanders

1
@dtanders Evet, bu da benim düşüncelerim, çiftler halinde görünmek zorundalar, eşleşmemiş vekil karakterler sadece kasıtlı olarak dizgiye koyarsanız ve eşleştirilmemişseniz gerçekleşir. Bilmediğim şey, diğer geliştiricilerin neden diziselleştirme yaklaşımını ( 3 yıldan fazla süredir kabul edilen bir cevap olan cevabım) kabul etmedikleri için kodlama farkında yaklaşımı kullanmamız gerektiğine neden olmaya devam etmesidir. vekil karakter bozulmamış. Ancak, kodlama özellikli çözümlerinin eşleştirilmemiş vekil karakteri de tutmadıklarını kontrol etmeyi unuttular, ironi ツ
Michael Buen

System.Buffer.BlockCopyDahili olarak kullanılan bir serileştirme kütüphanesi varsa , tüm kodlama-savunuculuk milletinin argümanları tartışılır
Michael Buen

2
@MichaelBuen Bana öyle geliyor ki asıl mesele, davalarında önemli olmadığını söylemek yerine, bir şeyin önemli olmadığını söyleyen büyük kalın harflerle yazılmış olmanızdır. Sonuç olarak, cevabınıza bakan insanları gelecekte başkalarının hayal kırıklığına uğramasına neden olacak temel programlama hataları yapmaya teşvik ediyorsunuz. Bir dizede eşleştirilmemiş vekiller geçersiz. Bir char dizisi değildir, bu nedenle bir dizeyi başka bir biçime dönüştürmenin FFFDo karakterde bir hataya neden olacağı mantıklıdır . Manuel dize düzenleme yapmak istiyorsanız, önerilen bir char [] kullanın.
Trisped

2
a: Değiştirilemez System.Stringbir dizidir Char; .NET, orijinal eşleştirilmemiş vekiller içeriyor olsa bile String, herhangi bir nesneden her zaman bir nesnenin oluşturulmasına Char[]ve içeriğini Char[]aynı değerleri içeren bir dosyaya vermesine izin vermiştir Char[].
supercat

41

Bunu deneyin, çok daha az kod:

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

Sonra bunu dene System.Text.Encoding.UTF8.GetBytes("Árvíztűrő tükörfúrógép);ve ağla! System.Text.Encoding.UTF8.GetBytes("Árvíztűrő tükörfúrógép").Length != System.Text.Encoding.UTF8.GetBytes("Arvizturo tukorfurogep").Length"Árvíztűrő tükörfúrógép".Length == "Arvizturo tukorfurogep".Length
Çalışacak

9
@ mg30rg: Örneğinizin neden garip olduğunu düşünüyorsunuz? Elbette, değişken genişlik kodlamasında tüm karakterlerin aynı bayt uzunlukları yoktur. Bunun nesi var?
Vlad

@Vlad Burada daha geçerli bir yorum, kodlanmış unicode sembolleri (bayt olarak) olarak, kendi aksanlarını içeren karakterlerin, karaktere eklenen değiştirici sembollere bölünen aksanlardan farklı bir sonuç vereceğidir . Ancak iirc, .net'te bunları özellikle bölmek, tutarlı bir bayt temsili elde etmek için yöntemler vardır.
Nyerguds

25

Tüm cevapları okudum ve eşleşmemiş vekilleri düşüren kodlama ya da serileştirme hakkındaydılar.

Örneğin, dize, örneğin bir parola karması depolayan bir bayt dizisi kullanılarak oluşturulduğu SQL Server'dan geldiğinde kötüdür . Ondan bir şey çıkarırsak, geçersiz bir karmayı depolar ve XML'de saklamak istiyorsak, bozulmadan bırakmak isteriz (çünkü XML yazarı bulduğu eşleştirilmemiş herhangi bir vekil için bir istisna bırakır).

Bu tür durumlarda bayt dizilerinin Base64 kodlamasını kullanıyorum , ama hey, internette C # 'da bunun tek bir çözümü var ve hata var ve sadece bir yol var, bu yüzden hatayı düzelttim ve geri yazdım prosedür. İşte gelecekteki Google ç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);
}

Bir bayt dizisini base64'e dönüştürmek için özel yönteminizi kullanmak yerine, tüm yapmanız gereken yerleşik dönüştürücüyü kullanmaktı: Convert.ToBase64String (arr);
Makotosan

@Makotosan teşekkür ederim, ancak Convert.ToBase64String(arr); base64 dönüşümleri için kullandım byte[] (data) <-> string (serialized data to store in XML file). Ama ilk almak için byte[] (data)bir şeyler yapmak için gerekli I Stringiçerdiği ikili veri (o MSSQL bana döndü yolu). Yani yukarıdaki fonksiyonlar içindir String (binary data) <-> byte[] (easy accessible binary data).
Gman

23

Ayrıca kodlamanın neden dikkate alınması gerektiğini de açıklayınız. Dize içinde saklanan baytları alamıyor muyum? Neden bu kodlamaya bağımlılık? !!!

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

Bir dize (veya daha genel olarak bir metin) karakterlerden oluşur: harfler, rakamlar ve diğer simgeler. Bu kadar. Ancak bilgisayarlar karakterler hakkında hiçbir şey bilmiyorlar; sadece baytlarla başa çıkabilirler. Bu nedenle, bir bilgisayar kullanarak metin depolamak veya iletmek istiyorsanız, karakterleri bayta dönüştürmeniz gerekir. Bunu nasıl yaptın? Kodlamalar burada devreye giriyor.

Kodlama, mantıksal karakterleri fiziksel baytlara çevirmek için kullanılan bir kuraldan başka bir şey değildir. En basit ve en iyi bilinen kodlama ASCII'dir ve İngilizce yazmanız gereken tek şey budur. Diğer diller için, daha eksiksiz kodlamalara ihtiyacınız olacak, Unicode lezzetlerinden herhangi biri bugünlerde en güvenli seçim.

Kısacası, "kodlama kullanmadan bir dizenin baytını almaya" çalışmak, "herhangi bir dil kullanmadan metin yazmak" kadar imkansızdır.

Bu arada, size (ve bu konudaki herhangi birine) bu küçük bilgeliği okumanızı şiddetle tavsiye ederim: Mutlak Minimum Her Yazılım Geliştiricisi Kesinlikle, Olumlu Unicode ve Karakter Setleri Hakkında Bilmelisiniz (Mazeret Yok!)


2
Açıklığa kavuşturmama izin verin: "Merhaba dünya" yı fiziksel baytlara çevirmek için bir kodlama kullanıldı. Dize bilgisayarımda saklandığından, bayt olarak saklanması gerektiğinden eminim. Sadece bu baytlara diskte veya başka bir nedenle kaydetmek için erişmek istiyorum. Bu baytları yorumlamak istemiyorum. Bu baytları yorumlamak istemediğim için, bu noktada bir kodlama ihtiyacı, printf'yi çağırmak için bir telefon hattı gerektirdiği kadar yanlış yerleştirilmiştir.
Agnel Kurian

3
Fakat yine de, bir kodlama kullanmadığınız sürece metin-fiziksel-bayt-çeviri kavramı yoktur. Elbette, derleyici dizeleri bir şekilde bellekte saklar - ancak sadece sizin veya derleyici geliştiricisi dışında kimsenin bilmediği bir dahili kodlama kullanır. Yani, ne yaparsanız yapın, bir dizeden fiziksel bayt almak için bir kodlamaya ihtiyacınız vardır.
Konamiman

@Agnel Kurian: Tabii ki, bir dizenin içeriğini depolayan bir yerde bir sürü bayt olduğu doğrudur (UTF-16 afair). Ancak erişmenizi engellemek için iyi bir neden vardır: dizeler değişmezdir ve dahili bayt [] dizisini elde edebiliyorsanız, onu da değiştirebilirsiniz. Bu, çok sayıda dizginin aynı verileri paylaşabilmesi nedeniyle hayati önem taşımaz. Dizeyi almak için UTF-16 kodlaması kullanmak, büyük olasılıkla verileri kopyalar.
ollb

2
@Gnafoo, Baytların bir kopyasını yapar.
Agnel Kurian

22

C # stringbir bytedizi dönüştürmek için :

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

17
byte[] strToByteArray(string str)
{
    System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
    return enc.GetBytes(str);
}

Ancak, kodlama neden dikkate alınmalıdır? Hangi kodlamanın kullanıldığını görmek zorunda kalmadan neden baytları alamıyorum? Gerekli olsa bile, String nesnesinin kendisi hangi kodlamanın kullanıldığını bilmemeli ve sadece bellekte ne olduğunu dökmemeli mi?
Agnel Kurian

5
Bu her zaman işe yaramaz. Bazı özel karakterler böyle bir yöntemi kullanarak kaybolabilir zor yolunu buldum.
JB King

17

Dize ve bayt dizisi arasında dönüştürme 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);

Bu bir sorunumu çözdü (bayt [] ff = ASCIIEncoding.ASCII.GetBytes (barcodetxt.Text);)
r.hamd

16

Gelişiyle birlikte Span<T>C # 7.2 ile piyasaya, kurallı bir tekniktir yönetilen bir bayt dizisi bir dize yatan hafıza temsilini yakalamak için:

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

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 tamlık uğruna:

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

İsimler NonPortableCastve DangerousGetPinnableReferencemuhtemelen bunu yapmamanız gerektiği argümanı olmalıdır.

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

Ne olursa olsun, gerçek orijinal soru ve takip eden yorumlar yatan bellek gösteren (ı değiştirilmiş ya da olduğu gibi-yazmaya gerek ötesine değil okuma aracı varsayalım) "yorumlanır" varlık değildir ima Bunun bazı uygulama Streamsınıfının veri hakkında akıl yürütme yerine dize olarak kullanılmalıdır.


13

Emin değilim, ama dize bayt ile verimsiz Chars bir dizi olarak bilgi depolar düşünüyorum. Özellikle, bir Char'ın tanımı "Unicode karakteri 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, UTF-8 cevabının birincisi için sadece 9 bayt ve ikincisi için sadece 7 bayt olduğunu unutmayın.

Bu nedenle, yalnızca dize tarafından kullanılan baytları istiyorsanız, sadece kullanın Encoding.Unicode, ancak depolama alanı ile verimsiz olacaktır.


10

Temel sorun, bir dizedeki glifin 32 bit (bir karakter kodu için 16 bit) alması, ancak bir baytın sadece 8 bit içermesidir. Kendinizi yalnızca ASCII karakterleri içeren dizelerle sınırlamadığınız sürece bire bir eşleme mevcut değildir. System.Text.Encoding öğesinin bir dizeyi bayt [] ile eşleştirmek için birçok yolu vardır, bilgi kaybını önleyen ve bayt [] öğesini bir dizeyle eşlemesi gerektiğinde müşteriniz tarafından kullanımı kolay olanı seçmeniz gerekir. .

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


3
UTF-8, yalnızca karakterlerinizin çoğu İngilizce (ASCII) karakter setindeyse kompakttır. Çince karakterlerden oluşan uzun bir dizeniz varsa, UTF-16, o dize için UTF-8'den daha kompakt bir kodlama olacaktır. Bunun nedeni UTF-8'in ASCII'yi kodlamak için bir bayt ve aksi takdirde 3 (veya belki 4) kullanmasıdır.
Joel Mueller

7
Doğru. Ancak, Çince metinlerle uğraşıyorsanız kodlama hakkında nasıl bilgi sahibi olamazsınız?
Hans Passant

9

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

OP özellikle belirli bir kodlamayı manuel olarak belirtmeden bir kodlama belirtmemesini ister ... "
Ferdz

8

En hızlı yol

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

Makotosan yorum olarak EDIT şimdi bu en iyi yolu olduğunu:

Encoding.UTF8.GetBytes(text)

8
ASCIIEncoding ..... gerekli değildir. Yalnızca Encoding.UTF8.GetBytes (metin) kullanılması tercih edilir.
Makotosan

8

Belirli bir kodlamayı el ile belirtmeden .NET (C #) içindeki bir dizeyi [] baytına nasıl dönüştürebilirim?

Bir dize , metni UTF-16 kod birimlerinin dizisi olarak temsil eder, bu nedenle baytlar zaten UTF-16'daki bellekte kodlanır.

Mehrdad'ın Cevabı

Mehrdad'ın cevabını kullanabilirsiniz , ancak karakter UTF-16 olduğu için aslında bir kodlama kullanır. Bu bakarak ToCharArray çağıran kaynağı bir oluşturur char[]doğrudan kendisine ve kopya bellek. Ardından, verileri ayrılan bir bayt dizisine kopyalar. Kaputun altında, alttaki baytları iki kez kopyalar ve çağrıdan sonra kullanılmayan bir char dizisi tahsis eder.

Tom Blodget kullanıcısının Yanıtı

Tom Blodget'ın cevabı , Mehrdad'dan % 20-30 daha hızlıdır, çünkü bir char dizisi tahsis etme ve baytları ona kopyalama ara adımını atlar, ancak bu /unsafeseçeneği derlemenizi gerektirir . Eğer kesinlikle kodlamayı kullanmak istemiyorsanız, bence bu yol. Şifreleme girişinizi fixedbloğun içine koyarsanız, ayrı bir bayt dizisi tahsis etmeniz ve baytları dizine kopyalamanız bile gerekmez.

Ayrıca, kodlama neden dikkate alınmalıdır? Dize içinde saklanan baytları alamıyor muyum? Neden karakter kodlamalarına bağımlılık var?

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

Geçersiz karakterlerle 'dizeleriniz' varsa, kodlama kullanmak size sorun çıkarabilir, ancak bu olmamalıdır. Dizenize geçersiz karakterlerle veri alıyorsanız, bunu yanlış yapıyorsunuz demektir. Başlamak için muhtemelen bir bayt dizisi veya Base64 kodlaması kullanıyor olmanız gerekir.

Kullanırsanız System.Text.Encoding.Unicode, kodunuz daha esnek olacaktır. Kodunuzun çalışacağı sistemin endianitesi hakkında endişelenmenize gerek yoktur . CLR'nin sonraki sürümünde farklı bir dahili karakter kodlaması kullanılacaksa endişelenmenize gerek yoktur.

Sorunun neden kodlama konusunda endişelenmek istediğiniz değil, neden onu görmezden gelip başka bir şey kullanmak istediğinizi düşündüğünü düşünüyorum. Kodlama, bir dizinin bayt dizisindeki soyutlamasını temsil eder. System.Text.Encoding.Unicodesize küçük bir endian bayt sırası kodlaması verecek ve her sistemde şimdi ve gelecekte de aynı işlemi gerçekleştirecektir.


Aslında C # 'daki bir dize sadece UTF-16 ile sınırlı DEĞİLDİR. Doğru olan, 16 bit kod birimleri vektörü içermesidir, ancak bu 16 bit kod birimleri geçerli UTF-16 ile sınırlı değildir. Ancak 16 bit olduklarından, bunları 8 bit'e dönüştürmek için bir kodlamaya (bayt sırası) ihtiyacınız vardır. Bir dize daha sonra ikili kod (örneğin bir bitmap görüntüsü) dahil olmak üzere Unicode olmayan verileri depolayabilir. Sadece I / O ve bu yorumu yapan metin formatlayıcılarında UTF-16 olarak yorumlanır.
verdy_p

Bu nedenle, bir C # dizesinde, UTF-16'da karakter olmayan olsalar bile 0xFFFF veya 0xFFFE gibi bir kod birimini güvenle saklayabilir ve 0xDC00..0xDFFF'de bir kod birimi izlemeyen izole bir 0xD800 saklayabilirsiniz (ör. UTF-16'da geçersiz eşleştirilmemiş taşıyıcılar). Aynı açıklama Javascript / ECMAscript ve Java dizeleri için de geçerlidir.
verdy_p

"GetBytes" kullandığınızda, elbette bir kodlama belirtmezsiniz, ancak dizede yerel olarak depolanan her kod birimi için iki baytı bir spesifikasyonda almak için bir bayt sırası olduğunu varsayarsınız. Baytlardan yeni bir dize oluşturduğunuzda, UTF-8 ila UTF-16 olmak zorunda olmayan bir dönüştürücüye de ihtiyacınız vardır, fazladan 0'ı yüksek bayta ekleyebilir veya iki bayt (ilk MSB veya LSB'de sırasıyla) paketleyebilirsiniz. aynı 16 bit kod birimi. Dizeler daha sonra 16 bitlik tamsayı dizileri için kompakt biçimdir. "Karakterler" ile ilişki başka bir sorundur, C # 'da hala dize olarak temsil edildiği için gerçek türler değildir
verdy_p

7

OP'nin sorusuna en yakın yaklaşım, aslında nesneye giren ve baytları çıkaran Tom Blodget's. String Object uygulamasına bağlı olduğu için en yakın diyorum.

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

Tabii, ama sorudaki temel hata burada ortaya çıkıyor. Dize, ilginç bir veri yapısına sahip olabilecek bir nesnedir. Bunu zaten biliyoruz, çünkü eşleştirilmemiş vekillerin saklanmasına izin veriyor. Uzunluğu depolayabilir. Hızlı saymaya izin veren 'eşleştirilmiş' her bir vekilin işaretçisi olabilir. Vb Bu ekstra baytların tümü karakter verilerinin bir parçası değildir.

İstediğiniz bir dizideki her karakterin bayt değeridir. İşte bu noktada 'kodlama' devreye giriyor. Varsayılan olarak UTF-16LE alacaksınız. Eğer gidiş-dönüş dışında baytların kendileri umursamıyorsanız, 'varsayılan' dahil herhangi bir kodlamayı seçebilir ve daha sonra geri dönüştürebilirsiniz (varsayılan kodlamanın ne olduğu, kod noktaları, hata düzeltmeleri gibi aynı parametreleri varsayarak) , eşleştirilmemiş vekiller gibi izin verilen şeyler.

Ama neden 'kodlamayı' büyüye bırakalım? Hangi byte'ları alacağınızı bilmek için neden kodlamayı belirtmiyorsunuz?

"Why is there a dependency on character encodings?"

Kodlama (bu bağlamda), dizenizi temsil eden baytlar anlamına gelir. Dize nesnesinin baytları değil. Dizenin saklandığı baytları istediniz - bu sorunun naif olarak sorulduğu yer. Dizenin baytını, dizeyi temsil eden bitişik bir dizide istediniz ve bir dize nesnesinin içerebileceği diğer tüm ikili verileri değil.

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

Tom Bloget'in cevabını seviyorum çünkü sizi 'string nesnesinin bayt' yönüne doğru götürdü. Yine de uygulamaya bağlıdır ve iç kısımlara baktığı için dizenin bir kopyasını yeniden oluşturmak zor olabilir.

Mehrdad'ın yanıtı yanlıştır çünkü kavramsal düzeyde yanıltıcıdır. Hala kodlanmış bir bayt listeniz var. Onun özel çözümü, eşleştirilmemiş vekillerin korunmasına izin verir - bu uygulamaya bağlıdır. Özel çözümü, dizeyi GetBytesvarsayılan olarak UTF-8'de döndürürse dizenin baytlarını doğru bir şekilde üretmez .


Bununla ilgili fikrimi değiştirdim (Mehrdad'ın çözümü) - bu ipin baytlarını almıyor; bunun yerine dizeden oluşturulan karakter dizisinin baytını alıyor. Kodlamaya bakılmaksızın, c # 'daki char veri türü sabit bir boyuttur. Bu, tutarlı bir uzunluk bayt dizisinin üretilmesine izin verir ve karakter dizisinin bayt dizisinin boyutuna göre çoğaltılmasına izin verir. Kodlama UTF-8 olsaydı, ancak her karakter en büyük utf8 değerini barındırmak için 6 bayt olsaydı, yine de işe yarardı. Gerçekten de - karakterin kodlanması önemli değil.

Ancak bir dönüşüm kullanıldı - her karakter sabit boyutlu bir kutuya yerleştirildi (c # 'ın karakter tipi). Ancak bu temsilin önemi yoktur, teknik olarak OP'nin cevabı budur. Öyleyse - yine de dönüştürecekseniz ... Neden 'kodlamıyor'?


Bu karakterler vardır desteklenmeyen exapmle için UTF-8 veya UTF-16 ve hatta UTF-32 tarafından: 񩱠& (Char) 55906& (Char) 55655. Yani yanlış olabilirsiniz ve Mehrdad'ın cevabı ne tür kodlamaların kullanıldığını düşünmeden güvenli bir dönüşümdür.
Mojtaba Rezaeian

Raymon, karakterler zaten bazı unicode değerlerle temsil edilir - ve tüm unicode değerler tüm utf'lar tarafından temsil edilebilir. Ne hakkında konuştuğunuz hakkında daha uzun bir açıklama var mı? Bu iki değer (veya 3 ..) hangi karakter kodlamasında var?
Gerard ONeill

Herhangi bir kodlama aralığı tarafından desteklenmeyen geçersiz karakterlerdir. Bu onların% 100 yararsız olduğu anlamına gelmez. Kodlamadan bağımsız olarak her tür dizeyi bayt dizisine eşdeğer hale getiren bir kod hiç yanlış bir çözüm değildir ve istenen durumlarda kendi kullanımları vardır.
Mojtaba Rezaeian

1
Tamam, sanırım sorunu anlamıyorsun. Unicode uyumlu bir dizi olduğunu biliyoruz - aslında .net olduğu için UTF-16 olduğunu biliyoruz. Yani bu karakterler orada olmayacak. Ayrıca iç temsilciliklerin değişmesi hakkındaki yorumumu tam olarak okumadınız. Dize, kodlanmış bir bayt dizisi değil, bir nesnedir. Bu yüzden son ifadenize katılmayacağım. Kodun tüm unicode dizelerini herhangi bir UTF kodlamasına dönüştürmesini istiyorsunuz. Bu, istediğinizi doğru bir şekilde yapar.
Gerard ONeill

Nesneler başlangıçta bir nesneyi geçerli durumunda tanımlayan veri dizisidir. Bu nedenle, programlama dillerindeki her veri bayt dizisine dönüştürülebilir (her bayt 8 biti tanımlar), çünkü herhangi bir nesnenin durumunu bellekte tutmanız gerekebilir. Bir bayt dizisini dosyaya veya belleğe kaydedip tutabilir ve diskten okuduktan sonra tamsayı, bigint, görüntü, Ascii dizesi, UTF-8 dizesi, şifreli dize veya kendi tanımlanmış veri türünüz olarak atayabilirsiniz. Böylece nesnelerin bayt dizisinden farklı bir şey olduğunu söyleyemezsiniz.
Mojtaba Rezaeian

6

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

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

3

Bir dizenin temel alınan baytlarının bir kopyasını gerçekten istiyorsanız, aşağıdaki gibi bir işlev kullanabilirsiniz. Ancak, nedenini öğrenmek için lütfen okumaya devam etmemelisiniz .

[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ında yatan baytların bir kopyasını oldukça hızlı bir şekilde alır. Bu baytları sisteminizde hangi şekilde kodlarlarsa alacaksınız. Bu kodlama neredeyse kesinlikle UTF-16LE'dir, ancak bu, dikkat etmeniz gerekmeyen bir uygulama detayıdır.

Sadece aramak daha güvenli, daha basit ve daha güvenilir olurdu ,

System.Text.Encoding.Unicode.GetBytes()

Herhalde bu aynı sonucu verecektir, yazılması daha kolaydır ve baytlar her zaman bir çağrı ile

System.Text.Encoding.Unicode.GetString()

3

İşte benim güvensiz uygulamasıdır Stringiçin Byte[]dönüşüm:

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

Oldukça zarif olmasa bile, kabul edilen anws'ınkinden çok daha hızlı. İşte 10000000 iterasyonundaki 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 özelliklerinizde "Güvenli Olmayan Kodlara İzin Ver" i işaretlemeniz gerekir. .NET Framework 3.5'e göre, bu yöntem Dize uzantısı olarak da kullanılabilir:

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

RuntimeHelpers.OffsetToStringData.NET'in Itanium sürümlerinde 8'in katlarının değeri var mı ? Aksi halde bu hizalanmamış okumalar nedeniyle başarısız olacaktır.
Jon Hanna

çağırmak daha kolay olmaz mıydı memcpy? stackoverflow.com/a/27124232/659190
Jodrell

2

Sadece şunu kullanın:

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

2
... ve atlama karakterleri 127'den yüksek olan tüm karakterleri kaybeder. Ana dilimde "Árvíztűrő tükörfúrógép" yazmak kesinlikle geçerlidir. System.Text.ASCIIEncoding.Default.GetBytes("Árvíztűrő tükörfúrógép.").ToString();geri "Árvizturo tukörfurogép."alınamayan bilgileri kaybedecektir. (Ve henüz tüm karakterleri kaybedeceğiniz Asya dillerinden bahsetmedim.)
mg30rg

2

Dize, aşağıdaki durumdan dolayı birkaç farklı yolla bayt dizisine dönüştürülebilir: .NET, Unicode'u destekler ve Unicode, UTF adı verilen birkaç fark kodlamasını standart hale getirir. Farklı uzunluklarda bayt gösterimine sahiptirler, ancak bir dize kodlandığında, dizeye geri kodlanabilir, ancak dize bir UTF ile kodlanırsa ve vidalanabilirse farklı UTF varsayımıyla kodu çözülürse eşdeğerdir. yukarı.

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

Şaşırtıcı olmayan bir şekilde, dizenin bir bayt ve serileştirme dizisine serileştirilmesi, System.Text.Encodingsoyut bir sınıf olan sınıf tarafından desteklenir ; türetilmiş sınıfları somut kodlamaları destekler: ASCIIEncodingve dört System.Text.UnicodeEncodingUTF ( UTF-16'yı destekler)

Bu bağlantıyı ref .

Kullanarak bir bayt dizisine serileştirme için System.Text.Encoding.GetBytes. Ters işlem için kullanın System.Text.Encoding.GetChars. Bu işlev bir karakter dizisi döndürür, bu nedenle bir dize almak için bir dize oluşturucu kullanın System.String(char[]).
Bu sayfayı ref.

Misal:

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

Baytların ne istediğine bağlıdır.

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

Bir dizede depolanmış ikili verileriniz (metin yerine) varsayarsak

Bu OP'nin kendi sorusuyla ilgili yorumunu temel alır ve OP'nin kullanım senaryosundaki ipuçlarını anlarsam doğru sorudur.

İkili verilerin dizelerde saklanması, yukarıda belirtilen varsayılan kodlama nedeniyle yanlış bir yaklaşımdır! Hangi program veya kütüphane bu ikili verileri string( byte[]daha uygun olacak bir dizi yerine) depolamışsa , savaş başlamadan önce savaşı kaybetmiştir. Baytları size bir REST isteği / yanıtı veya dizeleri iletmesi gereken herhangi bir şey olarak gönderiyorlarsa, Base64 doğru yaklaşım olacaktır.

Bilinmeyen kodlamaya sahip bir metin dizeniz varsa

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 bir kod), ilgili System.Text.Encoding.???.GetBytes()işlevi kullanın ve hangi kodlamayı seçtiğinize bayt verdiğinizi söyleyin.


2

Baytlarla ne yapmak istediğinizi sorduğunuzda, şu yanıtı verdiniz :

Şifreleyeceğim. Dönüştürmeden şifreleyebilirim ama yine de kodlamanın neden burada oynamaya geldiğini bilmek istiyorum. Sadece bana bayt ver dedim.

Bu şifrelenmiş verileri ağ üzerinden göndermek, daha sonra tekrar belleğe yüklemek veya başka bir işleme atmak isteyip istemediğinize bakılmaksızın, bir noktada şifresini çözmeyi planlıyorsunuz . Bu durumda cevap, bir iletişim protokolü tanımlamanızdır. Programlama dilinizin ve ilişkili çalışma zamanının uygulama ayrıntıları açısından bir iletişim protokolü tanımlanmamalıdır . Bunun birkaç nedeni vardır:

  • Farklı bir dilde veya çalışma zamanında uygulanan bir işlemle iletişim kurmanız gerekebilir. (Örneğin, başka bir makinede çalışan veya dizeyi bir JavaScript tarayıcı istemcisine gönderen bir sunucu içerebilir.)
  • Program gelecekte farklı bir dilde veya çalışma zamanında yeniden uygulanabilir.
  • .NET uygulaması, dizelerin iç temsilini değiştirebilir. Kulağa çok uzak geldiğini düşünebilirsiniz, ancak bu aslında Java 9'da bellek kullanımını azaltmak için oldu . .NET'in davayı izlememesinin bir nedeni yoktur. Skeet, UTF-16'nın günümüzde muhtemelen en uygun olmadığını, iç temsilin gelecekte değişme olasılığını artırarak, temsil etmek için 2 bayttan daha fazlasına ihtiyaç duyan emoji ve diğer Unicode bloklarının yükselişini verdiğini ileri sürüyor .

İletişim kurmak için (tamamen farklı bir süreçle veya gelecekte aynı programla), onunla çalışma zorluğunu en aza indirmek veya yanlışlıkla hatalar oluşturmak için protokolünüzü kesinlikle tanımlamanız gerekir . .NET'in dahili temsiline bağlı olarak, tutarlı bir tanım olduğu kesin, net ve hatta garanti edilemez. Standart kodlama , gelecekte başarısız olmayacak katı bir tanımdır.

Başka bir deyişle, bir kodlama belirtmeden tutarlılık gereksiniminizi karşılayamazsınız .

Sen olabilir kesinlikle bulursanız doğrudan UTF-16 kullanmayı tercih senin süreç gerçekleştirdiği önemli ölçüde daha iyi .NET içten ya da başka herhangi bir nedenden dolayı kullanır, ancak açıkça kodlayan seçip bağlı daha kodunuzda açıkça bu dönüşümleri oldukça gerçekleştirmek için ihtiyaç beri .NET'in dahili uygulamasında.

Bu yüzden bir kodlama seçin ve kullanın:

using System.Text;

// ...

Encoding.Unicode.GetBytes("abc"); # UTF-16 little endian
Encoding.UTF8.GetBytes("abc")

Gördüğünüz gibi, sadece dahili kodlama nesnelerini kullanmak kendi okuyucu / yazıcı yöntemlerinizi uygulamaktan daha az koddur.


1

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

Alttan yukarıdan daha sık kullanma eğilimindeyim, hız için kıyaslama yapmadım.


4
Çok baytlı karakterler ne olacak?
Agnel Kurian

c.ToByte () özeldir: S
Khodor

@AgnelKurian Msdn "Bu yöntem kendisine iletilen Char nesnesinin sayısal kodunu temsil eden işaretsiz bir bayt değeri döndürür. .NET Framework'te bir Char nesnesi 16 bitlik bir değerdir. Bu, yöntemin döndürmek için uygun olduğu anlamına gelir. ASCII karakter aralığındaki veya Unicode C0 Denetimleri ve Temel Latin'deki karakter kodlarının sayısal kodları ve U Denetimleri ile U + 00FF arasında C1 Denetimleri ve Latin-1 Ek aralıkları. "
mg30rg

1
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
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.