Pytanie W jaki sposób uzyskać spójną reprezentację bajtów ciągów w języku C # bez ręcznego określania kodowania?


Jak przekonwertować plik string do a byte[] w .NET (C #) bez ręcznego określania określonego kodowania?

Zamierzam zaszyfrować ciąg. Mogę zaszyfrować to bez konwersji, ale nadal chciałbym wiedzieć, dlaczego kodowanie przychodzi tutaj.

Ponadto, dlaczego powinno się brać pod uwagę kodowanie? Nie mogę po prostu zdobyć jakich bajtów przechowywany jest ciąg? Dlaczego istnieje zależność od kodowania znaków?


1912
2018-01-23 13:39


pochodzenie


Każdy ciąg jest przechowywany jako tablica bajtów, prawda? Dlaczego nie mogę po prostu mieć tych bajtów? - Agnel Kurian
Kodowanie jest co odwzorowuje znaki na bajty. Na przykład w ASCII litera "A" jest odwzorowywana na liczbę 65. W innym kodowaniu może nie być taka sama. Podejście wysokiego poziomu do łańcuchów podjętych w środowisku .NET sprawia, że ​​jest to w dużej mierze nieistotne (poza tym przypadkiem). - Lucas Jones
Aby zagrać w adwokata diabła: Jeśli chcesz zdobyć bajty ciągu w pamięci (tak, jak używa ich .NET) i manipulować nimi w jakiś sposób (np. CRC32), i NIGDY NIGDY nie chciałeś odszyfrować go z powrotem do oryginalnego ciągu ... nie jest proste, dlaczego interesowałbyś się kodowaniem lub wyborem, którego chcesz użyć. - Greg
Zaskoczony nikt jeszcze nie podał tego linka: joelonsoftware.com/articles/Unicode.html - Bevan
Znak char nie jest bajtem, a bajt nie jest char. Znak jest zarówno kluczem do tabeli czcionek, jak i tradycji leksykalnej. Ciąg to ciąg znaków. (Słowa, akapity, zdania i tytuły również mają własne tradycje leksykalne, które uzasadniają ich własne definicje typów - ale dygresja). Podobnie jak liczby całkowite, liczby zmiennoprzecinkowe i wszystko inne, znaki są kodowane w bajtach. Był czas, kiedy kodowanie było proste jeden do jednego: ASCII. Jednakże, aby pomieścić całą ludzką symbolikę, 256 permutacji bajtu było niewystarczające i opracowano kodowanie w celu wybiórczego wykorzystania większej liczby bajtów. - George


Odpowiedzi:


W przeciwieństwie do odpowiedzi tutaj, NIE musisz martwić się kodowaniem gdyby bajtów nie trzeba interpretować!

Jak wspomniałeś, twoim celem jest po prostu "uzyskaj liczbę bajtów, w których przechowywany jest ciąg".
(I oczywiście, aby móc ponownie skonstruować ciąg z bajtów).

Dla tych celów, naprawdę nie zrozumieć, dlaczego ludzie wciąż mówią ci, że potrzebujesz kodowania. Z pewnością nie musisz się martwić o kodowanie w tym celu.

Po prostu wykonaj to zamiast:

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

Tak długo jak twój program (lub inne programy) nie próbuj interpretować jakoś bajtów, o których oczywiście nie wspomniałeś, że masz zamiar zrobić, to jest nic źle z tym podejściem! Martwienie się kodowaniem sprawia, że ​​twoje życie staje się bardziej skomplikowane bez konkretnego powodu.

Dodatkowa korzyść z tego podejścia:

Nie ma znaczenia, czy ciąg znaków zawiera nieprawidłowe znaki, ponieważ nadal możesz uzyskać dane i zrekonstruować oryginalny ciąg!

Będzie to zakodowane i zdekodowane tak samo, ponieważ jesteś tylko patrząc na bajty.

Jeśli jednak używasz określonego kodowania, dałoby ci to kłopot z kodowaniem / dekodowaniem nieprawidłowych znaków.


1719
2018-04-30 07:44



To, co jest w tym brzydkie, to to GetString i GetBytes trzeba wykonać w systemie o tej samej endianness do pracy. Więc nie możesz tego użyć, aby uzyskać bajty, które chcesz przekształcić w ciąg w innym miejscu. Trudno mi więc wymyślić sytuacje, w których chciałbym to wykorzystać. - CodesInChaos
@CodeInChaos: Tak jak powiedziałem, chodzi o to, żeby użyć go w tym samym systemie, z tym samym zestawem funkcji. Jeśli nie, nie powinieneś go używać. - Mehrdad
-1 Gwarantuję, że ktoś (kto nie rozumie bajtów vs znaków) będzie chciał przekonwertować ich ciąg na tablicę bajtową, będą google go i przeczytać tę odpowiedź, a oni zrobią coś złego, ponieważ w prawie wszystkich przypadki, kodowanie JEST istotnych. - artbristol
@artbristol: Jeśli nie mogą być zainteresowani, aby przeczytać odpowiedź (lub inne odpowiedzi ...), to jest mi przykro, to nie ma lepszego sposobu, aby się z nimi komunikować. Generalnie wolę odpowiadać na PO zamiast próbować odgadnąć, co inni mogą zrobić z moją odpowiedzią - OP ma prawo wiedzieć, a tylko dlatego, że ktoś może nadużywać noża, nie oznacza, że ​​musimy ukrywać wszystkie noże na świecie dla nas. Chociaż jeśli się nie zgadzasz, to też jest w porządku. - Mehrdad
Ta odpowiedź jest błędna na wielu poziomach, ale przede wszystkim z powodu jej deklasyfikacji "NIE musisz martwić się kodowaniem!". Dwie metody, GetBytes i GetString są zbędne, ponieważ są jedynie powtórnymi implementacjami tego, co już robią Encoding.Unicode.GetBytes () i Encoding.Unicode.GetString (). Oświadczenie "Tak długo jak twój program (lub inne programy) nie próbują interpretować bajtów" jest również zasadniczo wadliwe, ponieważ niejawnie oznaczają one, że bajty powinny być interpretowane jako Unicode. - David


To zależy od kodowania twojego ciągu znaków (ASCII, UTF-8, ...).

Na przykład:

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

Mała próbka, dlaczego kodowanie ma znaczenie:

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 po prostu nie jest przystosowany do obsługi znaków specjalnych.

Wewnętrznie korzysta z platformy .NET UTF-16 do reprezentowania ciągów, więc jeśli chcesz po prostu uzyskać dokładne bajty, które używa .NET, użyj System.Text.Encoding.Unicode.GetBytes (...).

Widzieć Kodowanie znaków w .NET Framework (MSDN), aby uzyskać więcej informacji.


1052
2018-01-23 13:43



Ale dlaczego należy brać pod uwagę kodowanie? Dlaczego nie mogę po prostu uzyskać bajtów bez sprawdzania, jakie kodowanie jest używane? Nawet gdyby było to wymagane, czy obiekt String nie powinien wiedzieć, jakie kodowanie jest używane i po prostu zrzucić to, co jest w pamięci? - Agnel Kurian
Łańcuchy .NET są zawsze kodowane jako Unicode. Tak więc użyj System.Text.Encoding.Unicode.GetBytes (); aby uzyskać zestaw bajtów, które będzie używane przez .NET do reprezentowania znaków. Dlaczego miałbyś tego chcieć? Polecam UTF-8, zwłaszcza gdy większość postaci jest w zachodnim zestawie łacińskim. - AnthonyWJones
Również: dokładne bajty używane wewnętrznie w ciągu znaków nieważnejeśli system, który je pobiera, nie obsługuje tego kodowania lub traktuje go jako złe kodowanie. Jeśli wszystko jest w domenie .Net, po co konwertować na tablicę bajtów. W przeciwnym razie lepiej zachować wyraźne kodowanie - Joel Coehoorn
@Joel, Uważaj przy System.Text.Encoding.Default, ponieważ może być różny dla każdej uruchamianej maszyny. Dlatego zaleca się zawsze podawać kodowanie, na przykład UTF-8. - Ash
Nie potrzebujesz kodowania, chyba że ty (lub ktoś inny) rzeczywiście zamierza (-ją) interpretować dane, zamiast traktować je jako ogólny "blok bajtów". W przypadku takich rzeczy, jak kompresja, szyfrowanie itp., Martwienie się o kodowanie jest bez znaczenia. Widzieć moja odpowiedź aby to zrobić bez obawy o kodowanie. (Mogłem dać -1 za powiedzenie, że musisz martwić się kodowaniem, kiedy tego nie robisz, ale nie czuję się dzisiaj szczególnie podły.: P) - Mehrdad


Przyjęta odpowiedź jest bardzo, bardzo skomplikowana. Skorzystaj z dołączonych klas .NET do tego:

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

Nie wynajduj ponownie koła, jeśli nie musisz ...


245
2018-04-30 07:26



Przyjęta odpowiedź jest nie tylko bardzo skomplikowana, ale także recepta na katastrofę. - Konamiman
W przypadku, gdy zaakceptowana odpowiedź zostanie zmieniona, dla celów rekordowych, jest to odpowiedź Mehrdada w tym czasie i godzinie. Mamy nadzieję, że PO ponownie to przyjmie i zaakceptuje lepsze rozwiązanie. - Thomas Eding
dobre w zasadzie, ale kodowanie powinno być System.Text.Encoding.Unicode być odpowiednikiem odpowiedzi Mehrdada. - Jodrell
Pytanie zostało zredagowane o miliony razy od czasu oryginalnej odpowiedzi, więc może moja odpowiedź jest nieco przestarzała. Nigdy nie zamierzałem dać exace odpowiednika odpowiedzi Mehrdada, ale rozsądnie to zrobić. Ale możesz mieć rację. Jednak wyrażenie "get, w którym bajty były przechowywane w ciągu" w pierwotnym pytaniu, jest bardzo nieprecyzyjne. Przechowywany, gdzie? W pamięci? Na dysku? Jeśli w pamięci, System.Text.Encoding.Unicode.GetBytes prawdopodobnie byłaby bardziej precyzyjna. - Erik A. Brandstadmoen
@ Amissico, Twoja sugestia jest błędna, chyba że masz pewność, że Twój ciąg jest zgodny z domyślnym kodowaniem systemowym (ciąg znaków zawierający tylko znaki ASCII w domyślnym zestawie znaków starszego systemu). Ale nigdzie PO tego nie stwierdza. - Frédéric


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

105
2018-01-23 16:36



Można użyć tej samej instancji BinaryFormatter dla wszystkich tych operacji - Joel Coehoorn
Bardzo interesujące. Podobno spadnie jakikolwiek wysoki zastępczy znak Unicode. Zobacz dokumentację na [BinaryFormatter] - John Robertson
@ ErikA.Brandstadmoen Zobacz moje testy tutaj: stackoverflow.com/a/10384024 - Michael Buen


Musisz uwzględnić kodowanie, ponieważ 1 znak może być reprezentowany przez 1 albo więcej bajtów (do około 6), a różne kodowania będą traktować te bajty w różny sposób.

Joel ma post na ten temat:

Absolutne minimum Każdy programista absolutnie, pozytywnie musi wiedzieć o Unicode i zestawach znaków (bez wymówek!)


79
2018-01-23 14:03



"1 znak może być reprezentowany przez 1 lub więcej bajtów" Zgadzam się. Chcę tylko te bajty, niezależnie od tego, w jakim kodowaniu jest łańcuch. Jedyny sposób, w jaki łańcuch może być zapisany w pamięci, to bajty. Nawet znaki są przechowywane jako 1 lub więcej bajtów. Po prostu chcę dostać moje ręce na ich bajty. - Agnel Kurian
Nie potrzebujesz kodowania, chyba że ty (lub ktoś inny) rzeczywiście zamierza (-ją) interpretować dane, zamiast traktować je jako ogólny "blok bajtów". W przypadku takich rzeczy, jak kompresja, szyfrowanie itp., Martwienie się o kodowanie jest bez znaczenia. Widzieć moja odpowiedź aby to zrobić bez obawy o kodowanie. - Mehrdad
@Mehrdad - Całkowicie, ale pierwotne pytanie, o którym mówiłem, kiedy początkowo odpowiadałem, nie zaprzeczało temu, co OP miało się stać z tymi bajtami po ich konwersji, a dla przyszłych poszukiwaczy informacje na ten temat są istotne - to jest objęte Odpowiedź Joela całkiem przyjemnie - i jak państwo stwierdzacie w swojej odpowiedzi: pod warunkiem, że trzymacie się świata .NET i użyjecie swoich metod do konwersji do / z, jesteście szczęśliwi. Jak tylko wyjdziesz poza to, kodowanie będzie miało znaczenie. - Zhaph - Ben Duguid


To popularne pytanie. Ważne jest, aby zrozumieć, o co pyta autor, i że różni się ono od najpowszechniejszej potrzeby. Aby zniechęcić do niewłaściwego użycia kodu tam, gdzie nie jest to konieczne, odpowiedziałem mu później jako pierwszy.

Wspólna potrzeba

Każdy ciąg ma zestaw znaków i kodowanie. Po konwersji System.String obiekt do tablicy System.Byte nadal masz zestaw znaków i kodowanie. W przypadku większości zastosowań wiesz, jaki zestaw znaków i kodowanie potrzebujesz, a .NET ułatwia "kopiowanie z konwersją". Po prostu wybierz odpowiednie Encoding klasa.

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

Konwersja może wymagać obsługi przypadków, w których docelowy zestaw znaków lub kodowanie nie obsługuje znaków znajdujących się w źródle. Masz kilka opcji: wyjątek, podstawienie lub pominięcie. Domyślną zasadą jest zastąpienie "?".

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

Oczywiście konwersje niekoniecznie są bezstratne!

Uwaga: Dla System.String zestawem znaków źródłowych jest kod Unicode.

Jedyną mylącą rzeczą jest to, że .NET używa nazwy zestawu znaków dla nazwy jednego określonego kodowania tego zestawu znaków. Encoding.Unicode powinien zostać wywołany Encoding.UTF16.

To tyle w przypadku większości zastosowań. Jeśli tego właśnie potrzebujesz, przestań czytać tutaj. Zobacz zabawę Artykuł Joela Spolsky'ego jeśli nie rozumiesz, czym jest kodowanie.

Specyficzna potrzeba

Teraz autor pytania pyta: "Każdy ciąg jest przechowywany jako tablica bajtów, prawda? Dlaczego nie mogę po prostu mieć tych bajtów?"

On nie chce żadnej konwersji.

Od C # spec:

Przetwarzanie znaków i łańcuchów znaków w języku C # wykorzystuje kodowanie Unicode. Char   type reprezentuje jednostkę kodową UTF-16, a typ łańcucha oznacza a   sekwencja jednostek kodowych UTF-16.

Wiemy, że jeśli poprosimy o konwersję zerową (tj. Z UTF-16 do UTF-16), uzyskamy pożądany rezultat:

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

Ale aby uniknąć wzmianki o kodowaniu, musimy zrobić to w inny sposób. Jeśli pośredni typ danych jest akceptowalny, istnieje konceptualny skrót do tego:

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

To nie daje nam pożądanego typu danych, ale Odpowiedź Mehrdada pokazuje, jak przekonwertować tę tablicę Char do tablicy Byte za pomocą BlockCopy. Jednak to kopiuje łańcuch dwukrotnie! Ponadto, w sposób jawny wykorzystuje kod specyficzny dla kodowania: typ danych System.Char.

Jedynym sposobem na uzyskanie rzeczywistych bajtów, w których przechowywany jest ciąg, jest użycie wskaźnika. The fixed Instrukcja pozwala na przyjęcie adresu wartości. Ze specyfikacji C #:

[Dla] wyrażenie typu ciąg, ... inicjator oblicza   adres pierwszego znaku w ciągu znaków.

Aby to zrobić, kompilator pisze kod, pomijając pozostałe części obiektu łańcucha RuntimeHelpers.OffsetToStringData. Aby uzyskać surowe bajty, wystarczy utworzyć wskaźnik na łańcuchu i skopiować wymaganą liczbę bajtów.

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

Jak zauważył @CodesInChaos, wynik zależy od endianiczności urządzenia. Ale pytanie autora tego nie dotyczy.


76
2017-12-02 04:43



Ogólnie nie jest prawidłowe ustawienie byteCount do dwukrotnej długości ciągu. W przypadku punktów kodowych Unicode poza podstawową płaszczyzną wielojęzyczną, dla każdego znaku będą występować dwie 16-bitowe jednostki kodowe. - Jan Hettich
@Jan To prawda, ale długość łańcucha już podaje liczbę jednostek kodu (nie współrzędnych). - Tom Blodget
Dzięki za wskazanie tego! Z MSDN: "The Length własność String] zwraca liczbę Char obiekty w tym przypadku, a nie liczba znaków Unicode. "Twój przykładowy kod jest zatem poprawny, jak zapisano. - Jan Hettich
@TomBlodget: Interesujące, jeśli ktoś bierze instancje Globalization.SortKey, wyodrębnia KeyDatai pakuje wynikowe bajty z każdego do a String [dwa bajty na znak, Najpierw MSB], dzwonienie String.CompareOrdinal na wynikowych ciągach będzie znacznie szybciej niż wywoływanie SortKey.Compare w przypadku wystąpienia SortKeylub nawet dzwonić memcmp w tych przypadkach. Biorąc to pod uwagę, zastanawiam się, dlaczego KeyData zwraca a Byte[] zamiast a String? - supercat
@TomBlodget: Nie potrzebujesz fixed lub unsafe kod, możesz również zrobić var gch = GCHandle.Alloc("foo", GCHandleType.Pinned); var arr = new byte[sizeof(char) * ((string)gch.Target).Length]; Marshal.Copy(gch.AddrOfPinnedObject(), arr, 0, arr.Length); gch.Free(); - Mehrdad


Tylko po to, żeby pokazać dźwięk Mehrdrada odpowiedź działa, jego podejście może nawet przetrwać niesparowane znaki zastępcze(z których wiele zrównało się z moją odpowiedzią, z których wszyscy są jednakowo winni, np. System.Text.Encoding.UTF8.GetBytes, System.Text.Encoding.Unicode.GetBytes; te metody kodowania nie mogą utrzymywać wysokich znaków zastępczych d800na przykład, a te tylko zastępują wysokie znaki zastępcze wartością 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);
    }        
}

Wydajność:

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

Spróbuj z tym System.Text.Encoding.UTF8.GetBytes lub System.Text.Encoding.Unicode.GetBytes, zastąpią one tylko wysokie zastępcze znaki wartością fffd

Za każdym razem, gdy pojawia się ruch w tym pytaniu, wciąż myślę o serializatorze (czy to z Microsoft, czy z komponentu innego producenta), który może utrzymywać ciągi, nawet jeśli zawiera niepowiązane znaki zastępcze; Od czasu do czasu to google: serializacja niesparowana surogatka zastępcza .NET. To nie sprawia, że ​​tracę sen, ale to jest denerwujące, gdy od czasu do czasu ktoś komentuje moją odpowiedź, że jest wadliwy, ale ich odpowiedzi są równie wadliwe, jeśli chodzi o niesparowane znaki zastępcze.

Darn, Microsoft powinien właśnie wykorzystał System.Buffer.BlockCopy w jego BinaryFormatter ツ

谢谢!


35
2017-07-25 22:52



Czy surogat nie musi występować w parach, aby tworzyć ważne punkty kodowe? W takim przypadku mogę zrozumieć, dlaczego dane zostałyby zmanipulowane. - dtanders
@dtanders Tak, to też moje przemyślenia, muszą pojawiać się w parach, niesparowane znaki zastępcze właśnie się zdarzają, jeśli celowo umieszczasz je na sznurku i czynisz je niesparowanymi. To, czego nie wiem, to dlaczego inni deweloperzy nadal harlują, że powinniśmy stosować podejście uwzględniające kodowanie, ponieważ uważali oni podejście serializacyjne (moja odpowiedź, która była akceptowaną odpowiedzią przez ponad 3 lata) nie zachowuje nienaruszonej postaci zastępczej w stanie nienaruszonym. Ale zapomnieli sprawdzić, czy ich rozwiązania uwzględniające kodowanie nie utrzymują niesparowanej surogatki, ironia ツ - Michael Buen
Jeśli jest używana biblioteka do serializacji System.Buffer.BlockCopy wewnętrznie, wszystkie argumenty dotyczące kodowania-rzecznictwa będą dyskusyjne - Michael Buen
Problem z testem polega na tym, że podałeś niepoprawny ciąg. "W UTF-16, muszą zawsze pojawiać się w parach, jako wysoki zastępczy, po którym następuje niski odpowiednik, w ten sposób używając 32 bitów do oznaczenia jednego punktu kodowego.". Jeśli podążasz za / UD800 za pomocą / uDC00, to działa dobrze we wszystkich formatach unicode. Należy zauważyć, że jest to ciąg znaków, a nie tablica znaków, więc pewne ograniczenia mają sens. Ponadto działa poprawnie nawet bez / uDC00 w UTF7. - Trisped
@dtanders: A System.String jest niezmienną sekwencją Char; .NET zawsze zezwalał na String obiekt do skonstruowania z dowolnego Char[] i eksportuj jego zawartość do Char[] zawierające te same wartości, nawet jeśli oryginał Char[] zawiera niesparowane surogaty. - supercat


Wypróbuj to, o wiele mniej kodu:

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

34
2018-01-23 15:54



Spróbuj tego System.Text.Encoding.UTF8.GetBytes("Árvíztűrő tükörfúrógép);, i płacz! To zadziała, ale System.Text.Encoding.UTF8.GetBytes("Árvíztűrő tükörfúrógép").Length != System.Text.Encoding.UTF8.GetBytes("Arvizturo tukorfurogep").Length podczas "Árvíztűrő tükörfúrógép".Length == "Arvizturo tukorfurogep".Length - mg30rg
@ mg30rg: Jak myślisz, dlaczego Twój przykład jest dziwny? Z pewnością w kodowaniu o zmiennej szerokości nie wszystkie znaki mają takie same błędy bajtowe. Co jest z tym nie tak? - Vlad


Pierwsza część pytania (jak zdobyć bajty) została już odebrana przez innych: spójrz w System.Text.Encoding przestrzeń nazw.

Zajmę się następującym pytaniem: dlaczego musisz wybrać kodowanie? Dlaczego nie możesz tego uzyskać z samej klasy łańcuchów?

Odpowiedź jest podzielona na dwie części.

Przede wszystkim bajty używane wewnętrznie przez klasę string nieważne, a kiedy tylko podejmiesz decyzję, prawdopodobnie wprowadzisz błąd.

Jeśli Twój program znajduje się całkowicie w świecie .Net, nie musisz się martwić o to, że w ogóle uzyskasz tablice bajtów dla ciągów, nawet jeśli przesyłasz dane przez sieć. Zamiast tego użyj Serializacji .NET, aby martwić się o przesyłanie danych. Nie martwisz się już faktycznymi bajtami: formatator serializacji robi to za Ciebie.

Z drugiej strony, co jeśli przesyłasz te bajty gdzieś, których nie możesz zagwarantować, pobierze dane z serializowanego strumienia .Net? W tym przypadku zdecydowanie musisz się martwić o kodowanie, ponieważ oczywiście ten zewnętrzny system dba. A zatem, wewnętrzne bajty używane przez ciąg nie mają znaczenia: musisz wybrać kodowanie, aby można było wyraźnie o tym kodowaniu na końcu odbierającym, nawet jeśli jest to to samo kodowanie używane wewnętrznie przez .Net.

Rozumiem, że w tym przypadku możesz preferować użycie rzeczywistych bajtów przechowywanych przez zmienną łańcuchową w pamięci tam, gdzie to możliwe, z myślą, że może to zaoszczędzić trochę pracy, tworząc strumień bajtów. Jednak, mówię ci to po prostu nie jest ważne, w porównaniu do upewnienia się, że twoje dane wyjściowe są zrozumiałe na drugim końcu i do zagwarantowania, że musi wyraź swoje kodowanie. Dodatkowo, jeśli naprawdę chcesz dopasować swoje wewnętrzne bajty, możesz już po prostu wybrać Unicode kodowanie i uzyskaj oszczędności wydajności.

Co prowadzi mnie do drugiej części ... wybrania Unicode kodowanie jest mówienie .Net, aby używał bazowych bajtów. Musisz wybrać to kodowanie, ponieważ gdy pojawi się jakikolwiek nowo-szpakowaty Unicode-Plus, środowisko wykonawcze .Net musi mieć swobodę korzystania z tego nowszego, lepszego kodowania bez przerywania programu. Ale na razie (i przewidywalna przyszłość), wybór kodowania Unicode daje ci to, czego chcesz.

Ważne jest również zrozumienie, że twój ciąg znaków musi zostać ponownie napisany do drutu, a to wymaga przynajmniej częściowego przekształcenia wzoru bitowego nawet jeśli używasz pasującego kodowania. Komputer musi uwzględniać takie rzeczy, jak Big vs Little Endian, kolejność bajtów sieciowych, pakietowanie, informacje o sesji itp.


34
2018-03-10 08:57



Istnieją obszary w .NET, gdzie trzeba uzyskać tablice bajtów dla łańcuchów. Wiele klas .Cryptrography .NET zawiera metody takie jak ComputeHash (), które akceptują tablicę bajtów lub strumień. Nie masz innego wyjścia, jak najpierw przekonwertować ciąg na tablicę bajtów (wybierając kodowanie), a następnie opcjonalnie zawijając go w strumieniu. Jednak dopóki wybierzesz kodowanie (np. UTF8) z nim, nie ma z tym problemu. - Ash


Cóż, przeczytałem wszystkie odpowiedzi i chodziło o używanie kodowania lub o serializację, która upuszcza niesparowane surogaty.

Źle jest, gdy łańcuch, na przykład, pochodzi od SQL Server gdzie został zbudowany z tablicy bajtów, przechowującej na przykład hash hasła. Jeśli usuniemy z niej cokolwiek, będzie on przechowywał nieprawidłowy hasz i jeśli chcemy go przechowywać w XML, chcemy pozostawić go w stanie nienaruszonym (ponieważ program piszący XML upuszcza wyjątek na wszelkie niesparowane zastępcze, które znajdzie).

Więc używam Base64 kodowanie tablic bajtowych w takich przypadkach, ale hej, w Internecie jest tylko jedno rozwiązanie tego w C #, i ma błąd w nim i jest tylko jeden sposób, więc naprawiłem błąd i procedurę odpisaną. Oto ty, przyszli googlersi:

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

22
2017-07-16 11:45



Zamiast używać niestandardowej metody konwersji tablicy bajtów na base64, wystarczy użyć wbudowanego konwertera: Convert.ToBase64String (arr); - Makotosan
@Makotosan dziękuję, ale skorzystałem Convert.ToBase64String(arr);  dla konwersji base64 byte[] (data) <-> string (serialized data to store in XML file). Ale żeby uzyskać wstęp byte[] (data) Musiałem coś zrobić z String które zawierały dwójkowy dane (to sposób, w jaki MSSQL zwrócił mi go). WIĘC powyższe funkcje są dla String (binary data) <-> byte[] (easy accessible binary data). - Gman


Proszę również wyjaśnić, dlaczego należy wziąć pod uwagę kodowanie.   Nie mogę po prostu zdobyć jakich bajtów przechowywany jest ciąg?   Dlaczego ta zależność od kodowania? !!!

Ponieważ nie ma czegoś takiego jak "bajty ciągu".

Ciąg (lub bardziej ogólny, tekst) składa się ze znaków: liter, cyfr i innych symboli. To wszystko. Komputery jednak nie wiedzą nic o postaciach; mogą obsługiwać bajty. Dlatego jeśli chcesz przechowywać lub przesyłać tekst za pomocą komputera, musisz przekształcić znaki na bajty. Jak to robisz? Oto, gdzie przychodzą kodowania.

Kodowanie to nic innego jak konwencja do tłumaczenia znaków logicznych na bajty fizyczne. Najprostszym i najlepiej znanym kodowaniem jest ASCII i wszystko, czego potrzebujesz, to pisanie po angielsku. W przypadku innych języków będziesz potrzebować pełniejszego kodowania, będącego jednym z najchętniej wybieranych smaków Unicode.

Krótko mówiąc, próba "uzyskania bajtów ciągu bez użycia kodowania" jest tak samo niemożliwa jak "pisanie tekstu bez użycia jakiegokolwiek języka".

Przy okazji, zdecydowanie polecam (i każdemu, o to chodzi), aby przeczytać ten mały kawałek mądrości: Absolutne minimum Każdy programista absolutnie, pozytywnie musi wiedzieć o Unicode i zestawach znaków (bez wymówek!)


18
2018-06-05 10:52



Pozwól mi wyjaśnić: kodowanie zostało użyte do przetłumaczenia "Witaj świecie" na fizyczne bajty. Ponieważ ciąg jest przechowywany na moim komputerze, jestem pewien, że musi on być przechowywany w bajtach. Po prostu chcę uzyskać dostęp do tych bajtów, aby zapisać je na dysku lub z jakiegokolwiek innego powodu. Nie chcę interpretować tych bajtów. Ponieważ nie chcę interpretować tych bajtów, potrzeba kodowania w tym miejscu jest tak źle umieszczona, jak wymagająca linii telefonicznej do wywołania printf. - Agnel Kurian
Ale znowu nie ma koncepcji tłumaczenia tekstu na pamięć fizyczną, chyba że użyjesz kodowania. Oczywiście, kompilator przechowuje łańcuchy w jakiś sposób w pamięci - ale jest to po prostu użycie wewnętrznego kodowania, którego nie znasz (ani nikt poza programistą kompilatora). Tak więc, cokolwiek robisz, potrzebujesz kodowania, aby uzyskać fizyczne bajty z ciągu. - Konamiman
@Agnel Kurian: Jest prawdą, że ciąg ma kilka bajtów, które przechowują jego zawartość (UTF-16 afair). Istnieje jednak dobry powód, aby uniemożliwić ci dostęp do niego: ciągi znaków są niezmienne i jeśli mógłbyś uzyskać wewnętrzną tablicę bajtów [], możesz ją również zmodyfikować. To przełamuje niezmienność, co jest istotne, ponieważ wiele ciągów może mieć te same dane. Użycie kodowania UTF-16 do pobrania ciągu prawdopodobnie spowoduje skopiowanie danych. - ollb
@Gnafoo, zrobi to kopia bajtów. - Agnel Kurian