[c#] 인코딩을 수동으로 지정하지 않고 C #에서 문자열의 일관된 바이트 표현을 얻으려면 어떻게해야합니까?

특정 인코딩을 수동으로 지정하지 않고 stringa byte[]를 .NET (C #)으로 변환하려면 어떻게해야 합니까?

문자열을 암호화하겠습니다. 변환하지 않고 암호화 할 수는 있지만 여전히 인코딩이 왜 작동하는지 알고 싶습니다.

또한 인코딩을 고려해야하는 이유는 무엇입니까? 문자열이 저장된 바이트를 간단히 얻을 수 없습니까? 문자 인코딩에 의존하는 이유는 무엇입니까?



답변

여기의 답변과 달리 바이트를 해석 할 필요가없는 경우 인코딩에 대해 걱정할 필요가 없습니다 !

앞에서 언급했듯이 목표는 단순히 “문자열이 저장된 바이트“를 얻는 것 입니다.
(물론 바이트에서 문자열을 재구성 할 수도 있습니다.)

이러한 목표를 위해 사람들 이 왜 인코딩이 필요하다는 것을 계속 말하고 있는지 이해 하지 못합니다 . 이를 위해 인코딩에 대해 걱정할 필요는 없습니다.

대신이 작업을 수행하십시오.

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

프로그램 (또는 다른 프로그램)이 어떻게 든 바이트 를 해석 하려고 시도하지 않는 한 , 분명히 할 의도는 없지만 이 방법 에는 아무런 문제 가 없습니다 ! 인코딩에 대해 걱정하면 실제 이유없이 삶이 더 복잡해집니다.

이 접근 방식의 추가 이점 :

문자열에 유효하지 않은 문자가 포함되어 있더라도 데이터를 가져 와서 원래 문자열을 재구성 할 수 있기 때문에 중요하지 않습니다!

bytes 만보 고 있기 때문에 똑같이 인코딩되고 디코딩 됩니다 .

그러나 특정 인코딩을 사용한 경우 유효하지 않은 문자를 인코딩 / 디코딩하는 데 문제가있을 수 있습니다.


답변

문자열 인코딩 ( ASCII , UTF-8 , …) 에 따라 다릅니다 .

예를 들면 다음과 같습니다.

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

인코딩이 중요한 이유는 다음과 같습니다.

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는 단순히 특수 문자를 처리 할 수 ​​없습니다.

내부적으로 .NET 프레임 워크는 UTF-16 을 사용 하여 문자열을 나타내므로 .NET에서 사용하는 정확한 바이트를 얻으려면을 사용하십시오 System.Text.Encoding.Unicode.GetBytes (...).

자세한 내용 은 .NET Framework (MSDN) 의 문자 인코딩 을 참조하십시오.


답변

허용되는 답변은 매우 복잡합니다. 이를 위해 포함 된 .NET 클래스를 사용하십시오.

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

당신이 필요하지 않은 경우 바퀴를 재발 명하지 마십시오 …


답변

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


답변

1 개의 문자는 1 개 이상의 바이트 (최대 약 6 개) 로 표현 될 수 있으므로 인코딩을 고려해야 하며, 다른 인코딩은 이러한 바이트를 다르게 취급합니다.

Joel은 이것에 대해 글을 올렸습니다 :

절대적으로 모든 소프트웨어 개발자는 반드시 유니 코드 및 문자 집합에 대해 반드시 알아야합니다 (변명 없음).


답변

이것은 인기있는 질문입니다. 질문 작성자가 요구하는 내용을 이해하고 가장 일반적인 요구와 다른 점을 이해하는 것이 중요합니다. 필요하지 않은 코드의 오용을 막기 위해 나중에 먼저 답변했습니다.

일반적인 필요

모든 문자열에는 문자 세트와 인코딩이 있습니다. System.String객체를 배열 로 변환 System.Byte해도 여전히 문자 세트와 인코딩이 있습니다. 대부분의 용도에서 필요한 문자 세트와 인코딩을 알고 .NET을 사용하면 “변환하여 복사”하는 것이 간단 해집니다. 적절한 Encoding수업을 선택하십시오 .

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

변환은 대상 문자 세트 또는 인코딩이 소스에있는 문자를 지원하지 않는 경우를 처리해야합니다. 예외, 대체 또는 건너 뛰기 중에서 선택할 수 있습니다. 기본 정책은 ‘?’를 대체하는 것입니다.

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

분명히, 전환이 반드시 손실이있는 것은 아닙니다!

참고 : System.String소스 문자 세트의 경우 유니 코드입니다.

혼란스러운 점은 .NET이 해당 문자 세트의 특정 인코딩 이름에 문자 세트 이름을 사용한다는 것입니다. Encoding.Unicode호출되어야합니다 Encoding.UTF16.

그게 대부분의 사용법입니다. 그것이 필요한 것이라면 여기에서 읽기를 중단하십시오. 인코딩이 무엇인지 이해하지 못하는 경우 재미있는 Joel Spolsky 기사를 참조하십시오 .

특정 요구

이제 질문 작성자는 “모든 문자열이 바이트 배열로 저장됩니다. 왜 그런 바이트를 가질 수 없습니까?”

그는 어떤 전환도 원하지 않습니다.

로부터 C #을 사양 :

C #의 문자 및 문자열 처리는 유니 코드 인코딩을 사용합니다. char 유형은 UTF-16 코드 단위를 나타내고 문자열 유형은 일련의 UTF-16 코드 단위를 나타냅니다.

따라서 null 변환을 요청하면 (예 : UTF-16에서 UTF-16으로) 원하는 결과를 얻을 수 있습니다.

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

그러나 인코딩에 대한 언급을 피하려면 다른 방법으로 인코딩해야합니다. 중간 데이터 유형이 허용 가능한 경우 이에 대한 개념적 지름길이 있습니다.

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

그것은 우리에게 원하는 데이터 유형을 얻지 못하지만 Mehrdad의 대답BlockCopy를 사용 하여이 Char 배열을 바이트 배열로 변환하는 방법을 보여줍니다 . 그러나 이것은 문자열을 두 번 복사합니다! 또한 인코딩 관련 코드 인 datatype도 명시 적으로 사용합니다 System.Char.

문자열이 저장된 실제 바이트를 얻는 유일한 방법은 포인터를 사용하는 것입니다. 이 fixed문장은 값의 주소를 취할 수 있습니다. C # 사양에서 :

[문자열] 유형의 표현식의 경우, 초기화 프로그램은 문자열에서 첫 번째 문자의 주소를 계산합니다.

그렇게하기 위해 컴파일러는을 사용하여 문자열 객체의 다른 부분을 건너 뛰는 코드를 작성합니다 RuntimeHelpers.OffsetToStringData. 따라서 원시 바이트를 얻으려면 문자열에 대한 포인터를 만들고 필요한 바이트 수를 복사하십시오.

// 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가 지적했듯이 결과는 기계의 엔디안에 달려 있습니다. 그러나 질문 저자는 그것에 관심이 없습니다.


답변

귀하의 질문의 첫 번째 부분 (바이트를 얻는 방법)은 이미 다른 사람들에 의해 답변되었습니다 : System.Text.Encoding네임 스페이스를보십시오.

다음 질문에 답하겠습니다. 왜 인코딩을 선택해야합니까? 왜 문자열 클래스 자체에서 얻을 수 없습니까?

답은 두 부분으로되어 있습니다.

우선, 문자열 클래스 내부적으로 사용하는 바이트 는 중요하지 않으며 ,이를 가정 할 때마다 버그가 발생할 수 있습니다.

프로그램이 전적으로 .Net 세계에있는 경우 네트워크를 통해 데이터를 전송하더라도 문자열에 대한 바이트 배열을 얻는 것에 대해 걱정할 필요가 없습니다. 대신 .Net Serialization을 사용하여 데이터 전송에 대해 걱정하십시오. 더 이상 실제 바이트에 대해 걱정하지 않아도됩니다. Serialization 포맷터가이를 대신합니다.

반면에, 당신이 보장 할 수없는 어딘가에이 바이트를 보내면 .Net 직렬 스트림에서 데이터를 가져올 것입니까? 이 경우 분명히 외부 시스템이 관심을 갖기 때문에 인코딩에 대해 걱정할 필요가 있습니다. 다시 말하지만 문자열에 사용되는 내부 바이트는 중요하지 않습니다. 인코딩을 선택해야 .Net에서 내부적으로 사용하는 것과 동일한 인코딩이라도 수신 측에서이 인코딩에 대해 명시 적으로 지정할 수 있습니다.

이 경우 가능한 경우 메모리에 문자열 변수로 저장된 실제 바이트를 바이트 스트림을 만드는 일부 작업을 저장할 수 있다는 아이디어와 함께 사용하는 것이 좋습니다. 그러나 출력을 다른 쪽 끝에서 이해하고 인코딩으로 명시 적으로 보장 해야하는 것과 비교하여 중요하지 않습니다 . 또한 내부 바이트와 실제로 일치 시키려면 이미 Unicode인코딩을 선택하고 성능을 향상시킬 수 있습니다.

어느 따기 … 두 번째 부분에 저를 가져다 Unicode인코딩 되는 기본 바이트를 사용하는 닷넷 이야기. 새로운 인코딩 된 Unicode-Plus가 나올 때 .Net 런타임은 프로그램을 중단하지 않고이 새롭고 더 나은 인코딩 모델을 자유롭게 사용할 수 있어야하므로이 인코딩을 선택해야합니다. 그러나 당분간 (그리고 미래에도) 유니 코드 인코딩을 선택하면 원하는 것을 얻을 수 있습니다.

또한 문자열을 와이어로 다시 작성해야한다는 것을 이해하는 것이 중요 하며 일치하는 인코딩을 사용하는 경우에도 비트 패턴을 적어도 일부 변환 해야합니다 . 컴퓨터는 Big vs Little Endian, 네트워크 바이트 순서, 패킷 화, 세션 정보 등을 고려해야합니다.