당신은 어떻게 변환하는 바이트 배열을 진수 문자열이며,그 반대가?

StackOverflow https://stackoverflow.com/questions/311165

  •  10-07-2019
  •  | 
  •  

문제

할 수 있는 방법을 변환할 바이트 배열을 진수 문자열이며,그 반대가?

도움이 되었습니까?

해결책

어느 하나:

public static string ByteArrayToString(byte[] ba)
{
  StringBuilder hex = new StringBuilder(ba.Length * 2);
  foreach (byte b in ba)
    hex.AppendFormat("{0:x2}", b);
  return hex.ToString();
}

또는:

public static string ByteArrayToString(byte[] ba)
{
  return BitConverter.ToString(ba).Replace("-","");
}

예를 들어 더 많은 변형이 있습니다. 여기.

역전 변환은 다음과 같습니다.

public static byte[] StringToByteArray(String hex)
{
  int NumberChars = hex.Length;
  byte[] bytes = new byte[NumberChars / 2];
  for (int i = 0; i < NumberChars; i += 2)
    bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
  return bytes;
}

사용 Substring 조합 된 최상의 옵션입니다 Convert.ToByte. 보다 이 답변 자세한 내용은. 더 나은 성능이 필요하면 피해야합니다 Convert.ToByte 떨어 뜨리기 전에 SubString.

다른 팁

성능 분석

참고:새로운 지도자로서의 2015-08-20.

나는 각각의 전환 방법을 통해 어떤 원 Stopwatch 성능 테스트 실행과 임의의 문장(n=61,1000 반복)및 실행 프로젝트 Gutenburg 텍스트(n=1,238,957,150 번 반복).여기에 결과를 대략 가장 빠른에서 가장 느린.모든 측정 단위는 틱(10,000 틱=1ms 의)모든 상대 주에 비해[가장 느린] StringBuilder 구현합니다.을 위해 사용되는 코드는 아래를 참조하십시오 또는 테스트 framework repo 내가 이제 유지에 대한 코드 실행 중이다.

면책 조항

경고:에 의존하지 않는 이러한 통계는 아무것도에 대한 콘크리트;그들은 간단한 샘플을 실행 샘플 데이터를 확보합니다.당신이 정말로 필요한 경우 최고 수준의 성능을 테스트하십시오 이러한 방법 환경에서의 대표 제작 요구와 데이터의 대표 무엇을 사용하는 것입니다.

결과

조회 테이블이 있는 선도업체로 자리잡게 되 통해 바이트입니다.기본적으로,거기에 몇 가지 형태의 precomputing 어떤 주어 조금씩이나 바이트에 진수가 있습니다.그런 다음으로 찢어 데이터를 통해,당신은 단순히 보고 다음 부분은 무엇을 보고 진수 문자열이 있는 것 같습니다.그런 다음 이 값을 추가하는 결과 문자열을 출력에서 몇 가지 패션입니다.장시간 바이트를 조작,잠재적으로 더 열심히 읽고 몇몇에 의해 개발되었스 접근 방식이다.

당신의 최선의 방법은 여전히 있을 것을 찾는 일부 대표적인 데이터와 노력을 밖에서 생산과 같은 환경입니다.이 있는 경우 다양한 메모리 제약 조건이 좋을 수도 있습 방법을 적은 할당을 하는 것이 하나 빠지만 더 많은 메모리를 사용합니다.

코드를 테스트

으로 게임을 자유롭 테스트 코드를 사용합니다.버전은 여기에 포함되어 있지만 복합 repo 추가 자신의 방법이 있습니다.을 제출하시기 바랍 풀을 요청하는 경우 당신은 흥미로운 것을 발견하거나 개선하는 데 도움이 테스트 프레임워크에 그것을 사용합니다.

  1. 추가로 새로운 정체되는 방법(Func<byte[], string> 용)/테스트/ConvertByteArrayToHexString/Test.cs.
  2. 를 추가하는 방법의 이름을 TestCandidates 반환 값에는 같은 클래스입니다.
  3. 를 실행하고 있는지 확인 입력전,당신이 원하는 문장이나 텍스트를 설정/해제하여 의견에 GenerateTestInput 에서 같은 클래스입니다.
  4. Hit F5 대기 출력(HTML 덤프도에서 생성/bin 폴더).
static string ByteArrayToHexStringViaStringJoinArrayConvertAll(byte[] bytes) {
    return string.Join(string.Empty, Array.ConvertAll(bytes, b => b.ToString("X2")));
}
static string ByteArrayToHexStringViaStringConcatArrayConvertAll(byte[] bytes) {
    return string.Concat(Array.ConvertAll(bytes, b => b.ToString("X2")));
}
static string ByteArrayToHexStringViaBitConverter(byte[] bytes) {
    string hex = BitConverter.ToString(bytes);
    return hex.Replace("-", "");
}
static string ByteArrayToHexStringViaStringBuilderAggregateByteToString(byte[] bytes) {
    return bytes.Aggregate(new StringBuilder(bytes.Length * 2), (sb, b) => sb.Append(b.ToString("X2"))).ToString();
}
static string ByteArrayToHexStringViaStringBuilderForEachByteToString(byte[] bytes) {
    StringBuilder hex = new StringBuilder(bytes.Length * 2);
    foreach (byte b in bytes)
        hex.Append(b.ToString("X2"));
    return hex.ToString();
}
static string ByteArrayToHexStringViaStringBuilderAggregateAppendFormat(byte[] bytes) {
    return bytes.Aggregate(new StringBuilder(bytes.Length * 2), (sb, b) => sb.AppendFormat("{0:X2}", b)).ToString();
}
static string ByteArrayToHexStringViaStringBuilderForEachAppendFormat(byte[] bytes) {
    StringBuilder hex = new StringBuilder(bytes.Length * 2);
    foreach (byte b in bytes)
        hex.AppendFormat("{0:X2}", b);
    return hex.ToString();
}
static string ByteArrayToHexViaByteManipulation(byte[] bytes) {
    char[] c = new char[bytes.Length * 2];
    byte b;
    for (int i = 0; i < bytes.Length; i++) {
        b = ((byte)(bytes[i] >> 4));
        c[i * 2] = (char)(b > 9 ? b + 0x37 : b + 0x30);
        b = ((byte)(bytes[i] & 0xF));
        c[i * 2 + 1] = (char)(b > 9 ? b + 0x37 : b + 0x30);
    }
    return new string(c);
}
static string ByteArrayToHexViaByteManipulation2(byte[] bytes) {
    char[] c = new char[bytes.Length * 2];
    int b;
    for (int i = 0; i < bytes.Length; i++) {
        b = bytes[i] >> 4;
        c[i * 2] = (char)(55 + b + (((b - 10) >> 31) & -7));
        b = bytes[i] & 0xF;
        c[i * 2 + 1] = (char)(55 + b + (((b - 10) >> 31) & -7));
    }
    return new string(c);
}
static string ByteArrayToHexViaSoapHexBinary(byte[] bytes) {
    SoapHexBinary soapHexBinary = new SoapHexBinary(bytes);
    return soapHexBinary.ToString();
}
static string ByteArrayToHexViaLookupAndShift(byte[] bytes) {
    StringBuilder result = new StringBuilder(bytes.Length * 2);
    string hexAlphabet = "0123456789ABCDEF";
    foreach (byte b in bytes) {
        result.Append(hexAlphabet[(int)(b >> 4)]);
        result.Append(hexAlphabet[(int)(b & 0xF)]);
    }
    return result.ToString();
}
static readonly uint* _lookup32UnsafeP = (uint*)GCHandle.Alloc(_Lookup32, GCHandleType.Pinned).AddrOfPinnedObject();
static string ByteArrayToHexViaLookup32UnsafeDirect(byte[] bytes) {
    var lookupP = _lookup32UnsafeP;
    var result = new string((char)0, bytes.Length * 2);
    fixed (byte* bytesP = bytes)
    fixed (char* resultP = result) {
        uint* resultP2 = (uint*)resultP;
        for (int i = 0; i < bytes.Length; i++) {
            resultP2[i] = lookupP[bytesP[i]];
        }
    }
    return result;
}
static uint[] _Lookup32 = Enumerable.Range(0, 255).Select(i => {
    string s = i.ToString("X2");
    return ((uint)s[0]) + ((uint)s[1] << 16);
}).ToArray();
static string ByteArrayToHexViaLookupPerByte(byte[] bytes) {
    var result = new char[bytes.Length * 2];
    for (int i = 0; i < bytes.Length; i++)
    {
        var val = _Lookup32[bytes[i]];
        result[2*i] = (char)val;
        result[2*i + 1] = (char) (val >> 16);
    }
    return new string(result);
}
static string ByteArrayToHexViaLookup(byte[] bytes) {
    string[] hexStringTable = new string[] {
        "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B", "0C", "0D", "0E", "0F",
        "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1A", "1B", "1C", "1D", "1E", "1F",
        "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F",
        "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B", "3C", "3D", "3E", "3F",
        "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4A", "4B", "4C", "4D", "4E", "4F",
        "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", "5E", "5F",
        "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6A", "6B", "6C", "6D", "6E", "6F",
        "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7A", "7B", "7C", "7D", "7E", "7F",
        "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F",
        "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9A", "9B", "9C", "9D", "9E", "9F",
        "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF",
        "B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF",
        "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB", "CC", "CD", "CE", "CF",
        "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF",
        "E0", "E1", "E2", "E3", "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF",
        "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF",
    };
    StringBuilder result = new StringBuilder(bytes.Length * 2);
    foreach (byte b in bytes) {
        result.Append(hexStringTable[b]);
    }
    return result.ToString();
}

Update(2010-01-13)

추가 Waleed 의 대답을 분석합니다.매우 빠릅니다.

Update(2011-10-05)

추가 string.Concat Array.ConvertAll 변형에 대한 완전성(필요합니다.NET4.0).파 string.Join 버전입니다.

Update(2012-02-05)

테스트 repo 어 개 등 StringBuilder.Append(b.ToString("X2")).None 화가의 결과다. foreach 보다 빠르 {IEnumerable}.Aggregate, 예를 들어,지 BitConverter 여전히 승리합니다.

Update(2012-04-03)

추가 Mykroft 의 SoapHexBinary 대답을 분석했을 통해 세 번째 장소입니다.

Update(2013-01-15)

추가 CodesInChaos 의 바이트를 조작이 대답했 첫 번째 장소(의 큰 한계에서 큰 블록의 텍스트).

Update(2013-05-23)

추가 Nathan Moinvaziri 의 조회는 대답과 변형에서는 브라이언트의 블로그입니다.모두히 빨리,하지만 인도하는 테스트 기계 사용(는 보 9750).

Update(2014-07-31)

추가@CodesInChaos 의 새로운 바이트 기반 조회 대답이다.그것은 나타나를 촬영 한도에서 모두 문장을 테스트하고 전체 텍스트를 테스트합니다.

Update(2015-08-20)

추가 airbreather 의 최적화 unsafe 변 이 응답의 repo.는 경우에 재생하려면 안전하지 않은 게임을,당신이 얻을 수있는 몇 가지 성능 향상을 위해 이전상 수상자 모두에서 짧은 문자열과 대형습니다.

전화 수업이 있습니다 SOAPHEXBINGIN 그것은 당신이 원하는 것을 정확하게합니다.

using System.Runtime.Remoting.Metadata.W3cXsd2001;

public static byte[] GetStringToBytes(string value)
{
    SoapHexBinary shb = SoapHexBinary.Parse(value);
    return shb.Value;
}

public static string GetBytesToString(byte[] value)
{
    SoapHexBinary shb = new SoapHexBinary(value);
    return shb.ToString();
}

암호화 코드를 작성할 때 데이터 종속 타이밍이 측면 채널 공격으로 이어질 수 있으므로 데이터 종속 분기 및 테이블 조회를 피하는 것이 일반적입니다.

또한 매우 빠릅니다.

static string ByteToHexBitFiddle(byte[] bytes)
{
    char[] c = new char[bytes.Length * 2];
    int b;
    for (int i = 0; i < bytes.Length; i++) {
        b = bytes[i] >> 4;
        c[i * 2] = (char)(55 + b + (((b-10)>>31)&-7));
        b = bytes[i] & 0xF;
        c[i * 2 + 1] = (char)(55 + b + (((b-10)>>31)&-7));
    }
    return new string(c);
}

ph'nglui mglw'nafh cthulhu r'lyeh wgah'nagl fhtagn


여기에 들어가는 모든 희망을 버리십시오

이상한 비트 피들링에 대한 설명 :

  1. bytes[i] >> 4 바이트의 높은 니블을 추출합니다
    bytes[i] & 0xF 바이트의 낮은 니블을 추출합니다
  2. b - 10
    ~이다 < 0 값을 위해 b < 10, 이것은 십진수가 될 것입니다
    ~이다 >= 0 값을 위해 b > 10, 이것은 편지가 될 것입니다 A 에게 F.
  3. 사용 i >> 31 서명 된 32 비트 정수가 표지판 확장 덕분에 표시를 추출합니다. 그럴 것입니다 -1 ~을 위한 i < 0 그리고 0 ~을 위한 i >= 0.
  4. 결합 2)와 3)은 그것을 보여줍니다 (b-10)>>31 될거야 0 편지와 -1 숫자.
  5. 편지 사례를보고 마지막 요약이됩니다. 0, 그리고 b 10 ~ 15 범위에 있습니다. 우리는 그것을 A(65)까지 F(70), 이는 55를 추가한다는 것을 의미합니다.'A'-10).
  6. 숫자의 사례를 살펴보면 마지막 요약을 조정하여지도를 원합니다. b 범위 0에서 9에서 범위까지 0(48)까지 9(57). 이것은 -7이되어야한다는 것을 의미합니다.'0' - 55).
    이제 우리는 단지 7을 곱할 수 있지만 -1은 모든 비트가 1이기 때문에 대신 사용할 수 있습니다. & -7 ~부터 (0 & -7) == 0 그리고 (-1 & -7) == -7.

몇 가지 추가 고려 사항 :

  • 나는 두 번째 루프 변수를 사용하여 색인을 사용하지 않았습니다. c, 측정은이를 계산하는 것으로 나타났습니다 i 저렴합니다.
  • 정확히 사용합니다 i < bytes.Length 루프의 상한으로 인해 지터는 바운드 체크를 제거 할 수 있습니다. bytes[i], 그래서 나는 그 변형을 선택했습니다.
  • 만들기 b INT는 바이트에서 불필요한 변환을 허용합니다.

당신이 더 많은 유연성을 원한다면 BitConverter, 그러나 1990 년대 스타일의 명백한 루프를 원하지 않으면 다음을 수행 할 수 있습니다.

String.Join(String.Empty, Array.ConvertAll(bytes, x => x.ToString("X2")));

또는 .NET 4.0을 사용하는 경우 :

String.Concat(Array.ConvertAll(bytes, x => x.ToString("X2")));

(후자는 원래 게시물에 대한 의견에서.)

또 다른 조회 테이블 기반 접근법. 이것은 니블 당 조회 테이블 대신 각 바이트마다 하나의 조회 테이블 만 사용합니다.

private static readonly uint[] _lookup32 = CreateLookup32();

private static uint[] CreateLookup32()
{
    var result = new uint[256];
    for (int i = 0; i < 256; i++)
    {
        string s=i.ToString("X2");
        result[i] = ((uint)s[0]) + ((uint)s[1] << 16);
    }
    return result;
}

private static string ByteArrayToHexViaLookup32(byte[] bytes)
{
    var lookup32 = _lookup32;
    var result = new char[bytes.Length * 2];
    for (int i = 0; i < bytes.Length; i++)
    {
        var val = lookup32[bytes[i]];
        result[2*i] = (char)val;
        result[2*i + 1] = (char) (val >> 16);
    }
    return new string(result);
}

또한이 사용의 변형을 테스트했습니다 ushort, struct{char X1, X2}, struct{byte X1, X2} 조회 테이블에서.

컴파일 대상 (x86, x64)에 따라 대략 성능이 동일 하거나이 변형보다 약간 느 렸습니다.


그리고 더 높은 성능을 위해 unsafe 형제자매:

private static readonly uint[] _lookup32Unsafe = CreateLookup32Unsafe();
private static readonly uint* _lookup32UnsafeP = (uint*)GCHandle.Alloc(_lookup32Unsafe,GCHandleType.Pinned).AddrOfPinnedObject();

private static uint[] CreateLookup32Unsafe()
{
    var result = new uint[256];
    for (int i = 0; i < 256; i++)
    {
        string s=i.ToString("X2");
        if(BitConverter.IsLittleEndian)
            result[i] = ((uint)s[0]) + ((uint)s[1] << 16);
        else
            result[i] = ((uint)s[1]) + ((uint)s[0] << 16);
    }
    return result;
}

public static string ByteArrayToHexViaLookup32Unsafe(byte[] bytes)
{
    var lookupP = _lookup32UnsafeP;
    var result = new char[bytes.Length * 2];
    fixed(byte* bytesP = bytes)
    fixed (char* resultP = result)
    {
        uint* resultP2 = (uint*)resultP;
        for (int i = 0; i < bytes.Length; i++)
        {
            resultP2[i] = lookupP[bytesP[i]];
        }
    }
    return new string(result);
}

또는 직접 문자열에 쓰는 것이 허용되는 경우 :

public static string ByteArrayToHexViaLookup32UnsafeDirect(byte[] bytes)
{
    var lookupP = _lookup32UnsafeP;
    var result = new string((char)0, bytes.Length * 2);
    fixed (byte* bytesP = bytes)
    fixed (char* resultP = result)
    {
        uint* resultP2 = (uint*)resultP;
        for (int i = 0; i < bytes.Length; i++)
        {
            resultP2[i] = lookupP[bytesP[i]];
        }
    }
    return result;
}

bitconverter.toString 메소드를 사용할 수 있습니다.

byte[] bytes = {0, 1, 2, 4, 8, 16, 32, 64, 128, 256}
Console.WriteLine( BitConverter.ToString(bytes));

산출:

00-01-02-04-08-10-20-40-80-FF

추가 정보: bitconverter.toString 메소드 (byte [])

오늘 방금 똑같은 문제를 겪었고이 코드를 발견했습니다.

private static string ByteArrayToHex(byte[] barray)
{
    char[] c = new char[barray.Length * 2];
    byte b;
    for (int i = 0; i < barray.Length; ++i)
    {
        b = ((byte)(barray[i] >> 4));
        c[i * 2] = (char)(b > 9 ? b + 0x37 : b + 0x30);
        b = ((byte)(barray[i] & 0xF));
        c[i * 2 + 1] = (char)(b > 9 ? b + 0x37 : b + 0x30);
    }
    return new string(c);
}

출처 : 포럼 게시물 바이트 [] 배열로 16 진 문자열 (Pzahra의 게시물 참조). 0x 접두사를 제거하기 위해 코드를 조금 수정했습니다.

코드에 대한 성능 테스트를 수행했으며 Bitconverter.toString () (Patridge의 게시물에 따라 가장 빠른)를 사용하는 것보다 거의 8 배 더 빠릅니다.

이 문제는 조회 테이블을 사용하여 해결할 수도 있습니다. 이를 위해서는 인코더와 디코더 모두에 대해 소량의 정적 메모리가 필요합니다. 그러나이 방법은 빠릅니다.

  • 인코더 표 512 바이트 또는 1024 바이트 (상단 및 소문자가 모두 필요한 경우 크기의 두 배)
  • 디코더 표 256 바이트 또는 64 kib (단일 char 룩업 또는 듀얼 숯 룩업)

내 솔루션은 인코딩 테이블에 1024 바이트를 사용하고 디코딩에는 256 바이트를 사용합니다.

디코딩

private static readonly byte[] LookupTable = new byte[] {
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};

private static byte Lookup(char c)
{
  var b = LookupTable[c];
  if (b == 255)
    throw new IOException("Expected a hex character, got " + c);
  return b;
}

public static byte ToByte(char[] chars, int offset)
{
  return (byte)(Lookup(chars[offset]) << 4 | Lookup(chars[offset + 1]));
}

부호화

private static readonly char[][] LookupTableUpper;
private static readonly char[][] LookupTableLower;

static Hex()
{
  LookupTableLower = new char[256][];
  LookupTableUpper = new char[256][];
  for (var i = 0; i < 256; i++)
  {
    LookupTableLower[i] = i.ToString("x2").ToCharArray();
    LookupTableUpper[i] = i.ToString("X2").ToCharArray();
  }
}

public static char[] ToCharLower(byte[] b, int bOffset)
{
  return LookupTableLower[b[bOffset]];
}

public static char[] ToCharUpper(byte[] b, int bOffset)
{
  return LookupTableUpper[b[bOffset]];
}

비교

StringBuilderToStringFromBytes:   106148
BitConverterToStringFromBytes:     15783
ArrayConvertAllToStringFromBytes:  54290
ByteManipulationToCharArray:        8444
TableBasedToCharArray:              5651 *

*이 솔루션

메모

디코딩 중에 ioException 및 IndexOutOfRangeException이 발생할 수 있습니다 (문자의 값이 너무 높으면> 256). 스트림 또는 배열을 인코딩하는 방법을 구현해야합니다. 이는 개념 증명 일뿐입니다.

이것은 답입니다 개정 4Tomalak의 인기있는 대답 (및 후속 편집).

나는이 편집이 잘못되었고 왜 그것을 되돌릴 수 있는지 설명 할 것이다. 그 과정에서, 당신은 일부 내부에 대해 한두 가지를 배우고, 조기 최적화가 실제로 무엇인지, 그리고 그것이 당신을 물린 방법에 대한 또 다른 예를 볼 수 있습니다.

tl; dr : 그냥 사용하십시오 Convert.ToByte 그리고 String.Substring 서두르고 있다면 (아래의 "원본 코드") 재 구현을 원하지 않는다면 가장 좋은 조합입니다. Convert.ToByte. 사용하지 않는 더 진보 된 것 (다른 답변 참조)을 사용하십시오. Convert.ToByte 만약 너라면 필요 성능. 하다 ~ 아니다 다른 것을 사용하십시오 String.Substring 와 함께 Convert.ToByte, 이 답변의 의견에서 누군가가 이것에 대해 흥미로운 말을하지 않는 한.

경고: 이 대답은 쓸모 없게 될 수 있습니다 만약에Convert.ToByte(char[], Int32) 오버로드는 프레임 워크에서 구현됩니다. 이것은 곧 일어나지 않을 것입니다.

일반적으로 "조기"가 언제인지 모르는 사람이 없기 때문에 "조기에 최적화하지 마십시오"라고 말하고 싶지 않습니다. 최적화 여부를 결정할 때 고려해야 할 유일한 것은 다음과 같습니다. "최적화 접근 방식을 올바르게 조사 할 시간과 리소스가 있습니까?" 그렇지 않으면 너무 빨리, 프로젝트가 더 성숙해질 때까지 기다리거나 성능이 필요할 때까지 기다립니다 (실제 필요가 있다면 만들다 시간). 그 동안 대신 작동 할 수있는 가장 간단한 일을하십시오.

원본 코드 :

    public static byte[] HexadecimalStringToByteArray_Original(string input)
    {
        var outputLength = input.Length / 2;
        var output = new byte[outputLength];
        for (var i = 0; i < outputLength; i++)
            output[i] = Convert.ToByte(input.Substring(i * 2, 2), 16);
        return output;
    }

개정 4 :

    public static byte[] HexadecimalStringToByteArray_Rev4(string input)
    {
        var outputLength = input.Length / 2;
        var output = new byte[outputLength];
        using (var sr = new StringReader(input))
        {
            for (var i = 0; i < outputLength; i++)
                output[i] = Convert.ToByte(new string(new char[2] { (char)sr.Read(), (char)sr.Read() }), 16);
        }
        return output;
    }

개정은 피합니다 String.Substring 그리고 a StringReader 대신에. 주어진 이유는 다음과 같습니다.

편집 : 단일 패스 파서를 사용하여 긴 문자열의 성능을 향상시킬 수 있습니다.

글쎄, 참조 코드 String.Substring, 그것은 이미 "단일 패스"입니다. 그리고 왜 그렇게되어서는 안됩니까? 대리 쌍이 아닌 바이트 수준에서 작동합니다.

그러나 새 문자열을 할당하지만 전달할 하나를 할당해야합니다. Convert.ToByte 그래도. 또한, 개정에 제공된 솔루션은 모든 반복 (2 차 배열)에 또 다른 객체를 할당합니다. 해당 할당을 루프 외부에 안전하게 넣고 배열을 재사용하여 피할 수 있습니다.

    public static byte[] HexadecimalStringToByteArray(string input)
    {
        var outputLength = input.Length / 2;
        var output = new byte[outputLength];
        var numeral = new char[2];
        using (var sr = new StringReader(input))
        {
            for (var i = 0; i < outputLength; i++)
            {
                numeral[0] = (char)sr.Read();
                numeral[1] = (char)sr.Read();
                output[i] = Convert.ToByte(new string(numeral), 16);
            }
        }
        return output;
    }

각 16 진수 numeral 두 자리 (기호)를 사용하여 단일 옥트를 나타냅니다.

그러나 왜 전화를 받는가 StringReader.Read 두 배? 두 번째 오버로드를 호출하고 2 char 어레이에서 한 번에 두 문자를 읽도록 요청하십시오. 통화 금액을 2 씩 줄입니다.

    public static byte[] HexadecimalStringToByteArray(string input)
    {
        var outputLength = input.Length / 2;
        var output = new byte[outputLength];
        var numeral = new char[2];
        using (var sr = new StringReader(input))
        {
            for (var i = 0; i < outputLength; i++)
            {
                var read = sr.Read(numeral, 0, 2);
                Debug.Assert(read == 2);
                output[i] = Convert.ToByte(new string(numeral), 16);
            }
        }
        return output;
    }

남은 것은 "value"만 추가 한 문자열 리더입니다. _pos) 당신이 자신을 선언 할 수 있었던 (AS j 예를 들어), 중복 길이 변수 (내부 _length) 및 입력 문자열에 대한 중복 참조 (내부 _s). 다시 말해, 그것은 쓸모가 없습니다.

어떻게 궁금하다면 Read "읽기", 그냥보세요 코드, 그것이하는 것은 전화뿐입니다 String.CopyTo 입력 문자열에서. 나머지는 우리가 필요없는 가치를 유지하기 위해 단지 부기 오버 헤드입니다.

따라서 이미 문자열 리더를 제거하고 CopyTo 당신 자신; 더 단순하고 명확하며 효율적입니다.

    public static byte[] HexadecimalStringToByteArray(string input)
    {
        var outputLength = input.Length / 2;
        var output = new byte[outputLength];
        var numeral = new char[2];
        for (int i = 0, j = 0; i < outputLength; i++, j += 2)
        {
            input.CopyTo(j, numeral, 0, 2);
            output[i] = Convert.ToByte(new string(numeral), 16);
        }
        return output;
    }

당신은 정말로 필요합니까? j 2 단계의 단계에서 증가하는 색인 i? 물론 그냥 곱하십시오 i 두 개 (컴파일러가 추가로 최적화 할 수 있어야 함).

    public static byte[] HexadecimalStringToByteArray_BestEffort(string input)
    {
        var outputLength = input.Length / 2;
        var output = new byte[outputLength];
        var numeral = new char[2];
        for (int i = 0; i < outputLength; i++)
        {
            input.CopyTo(i * 2, numeral, 0, 2);
            output[i] = Convert.ToByte(new string(numeral), 16);
        }
        return output;
    }

솔루션은 지금 어떻게 생겼습니까? 처음에 있었던 것처럼 사용하는 대신 String.Substring 문자열을 할당하고 데이터를 복사하려면 16 진수 숫자를 복사하는 중개 배열을 사용하여 문자열을 직접 할당하고 데이터를 복사합니다. 다시 배열에서 문자열로 (문자열 생성자로 전달할 때). 문자열이 이미 인턴 풀에 있으면 두 번째 사본이 최적화 될 수 있지만 String.Substring 이 경우에도 피할 수 있습니다.

사실, 당신이 보면 String.Substring 다시 말하지만, 문자열이 정상적으로 수행 할 수있는 것보다 더 빨리 문자열을 할당하기 위해 문자열이 구성되는 방법에 대한 낮은 수준의 내부 지식을 사용하고 있으며 CopyTo 통화 오버 헤드를 피하기 위해 직접.

String.Substring

  • 최악의 경우 : 하나의 빠른 할당, 하나의 빠른 사본.
  • 최고 사례 : 할당 없음, 사본 없음.

수동 방법

  • 최악의 경우 : 두 개의 정상 할당, 1 개의 정상 사본, 하나의 빠른 사본.
  • 최고 사례 : 하나의 정상 할당, 하나의 정상 사본.

결론? 사용하려면 Convert.ToByte(String, Int32) (그 기능을 직접 재 구현하고 싶지 않기 때문에), 이길 수있는 방법이없는 것 같습니다. String.Substring; 당신이하는 모든 일은 원으로 실행되며 바퀴를 다시 발명합니다 (하위 최적 재료만으로).

사용하십시오 Convert.ToByte 그리고 String.Substring 극단적 인 성능이 필요하지 않은 경우 완벽하게 유효한 선택입니다. 기억하십시오 : 올바르게 작동하는 방법을 조사 할 시간과 자원이있는 경우 대안 만 선택하십시오.

있다면 Convert.ToByte(char[], Int32), 물론 상황이 다를 것입니다 (위에서 설명한 것을 수행하고 완전히 피할 수 있습니다. String).

나는 "피하는"더 나은 성과를보고하는 사람들이 String.Substring"또한 피하십시오 Convert.ToByte(String, Int32), 어쨌든 성능이 필요하다면 실제로해야합니다. 수많은 다른 답변을보고 그렇게 할 모든 다른 접근법을 발견하십시오.

면책 조항 : 참조 소스가 최신 상태인지 확인하기 위해 최신 버전의 프레임 워크를 분해하지 않았습니다.

이제, 그것은 모두 좋고 논리적으로 들리며, 당신이 지금까지 얻을 수 있었을 때도 분명하게 분명합니다. 하지만 사실인가요?

Intel(R) Core(TM) i7-3720QM CPU @ 2.60GHz
    Cores: 8
    Current Clock Speed: 2600
    Max Clock Speed: 2600
--------------------
Parsing hexadecimal string into an array of bytes
--------------------
HexadecimalStringToByteArray_Original: 7,777.09 average ticks (over 10000 runs), 1.2X
HexadecimalStringToByteArray_BestEffort: 8,550.82 average ticks (over 10000 runs), 1.1X
HexadecimalStringToByteArray_Rev4: 9,218.03 average ticks (over 10000 runs), 1.0X

예!

벤치 프레임 워크를 위해 Partridge에 소품을 해킹하기 쉽습니다. 사용 된 입력은 다음과 같은 SHA-1 해시가 5000 번 반복되어 100,000 바이트가 길고 긴 문자열을 만듭니다.

209113288F93A9AB8E474EA78D899AFDBB874355

재미있게 보내세요! (그러나 적당히 최적화하십시오.)

@codesinchaos의 답변 보완 (역전 메소드)

public static byte[] HexToByteUsingByteManipulation(string s)
{
    byte[] bytes = new byte[s.Length / 2];
    for (int i = 0; i < bytes.Length; i++)
    {
        int hi = s[i*2] - 65;
        hi = hi + 10 + ((hi >> 31) & 7);

        int lo = s[i*2 + 1] - 65;
        lo = lo + 10 + ((lo >> 31) & 7) & 0x0f;

        bytes[i] = (byte) (lo | hi << 4);
    }
    return bytes;
}

설명:

& 0x0f 소문자도 지원하는 것입니다

hi = hi + 10 + ((hi >> 31) & 7); 와 같다:

hi = ch-65 + 10 + (((ch-65) >> 31) & 7);

'0'.. '9'의 경우와 동일합니다. hi = ch - 65 + 10 + 7; 그게 hi = ch - 48 (이것은 때문입니다 0xffffffff & 7).

'a'.. 'f'를 위해 hi = ch - 65 + 10; (이것은 때문입니다 0x00000000 & 7).

'a'.. 'f'를 위해 우리는 큰 숫자로 가야하므로 비트를 만들어 기본 버전에서 32를 빼야합니다. 0 사용하여 & 0x0f.

65는 코드입니다 'A'

48은 코드입니다 '0'

7은 그 사이의 글자 수입니다 '9' 그리고 'A' ASCII 테이블에서 (...456789:;<=>?@ABCD...).

이것은 훌륭한 게시물입니다. 나는 Waleed의 해결책을 좋아합니다. 나는 Patridge의 테스트를 통해 그것을 실행하지 않았지만 매우 빠른 것 같습니다. 또한 16 진수를 바이트 어레이로 변환하여 리버스 프로세스가 필요했기 때문에 Waleed 솔루션의 역전으로 썼습니다. Tomalak의 원래 솔루션보다 빠른지 확실하지 않습니다. 다시, 나는 Patridge의 테스트를 통해 역 프로세스를 실행하지 않았습니다.

private byte[] HexStringToByteArray(string hexString)
{
    int hexStringLength = hexString.Length;
    byte[] b = new byte[hexStringLength / 2];
    for (int i = 0; i < hexStringLength; i += 2)
    {
        int topChar = (hexString[i] > 0x40 ? hexString[i] - 0x37 : hexString[i] - 0x30) << 4;
        int bottomChar = hexString[i + 1] > 0x40 ? hexString[i + 1] - 0x37 : hexString[i + 1] - 0x30;
        b[i / 2] = Convert.ToByte(topChar + bottomChar);
    }
    return b;
}

왜 복잡하게 만드는가? 이것은 Visual Studio 2008에서 간단합니다.

씨#:

string hex = BitConverter.ToString(YourByteArray).Replace("-", "");

VB :

Dim hex As String = BitConverter.ToString(YourByteArray).Replace("-", "")

여기에 많은 답변을 쌓지 말고, 나는 상당히 최적의 (~ 4.5 배, 허용되는 것보다 낫다), Hex String Parser의 간단한 구현을 발견했습니다. 첫째, 내 테스트의 출력 (첫 번째 배치는 내 구현입니다) :

Give me that string:
04c63f7842740c77e545bb0b2ade90b384f119f6ab57b680b7aa575a2f40939f

Time to parse 100,000 times: 50.4192 ms
Result as base64: BMY/eEJ0DHflRbsLKt6Qs4TxGfarV7aAt6pXWi9Ak58=
BitConverter'd: 04-C6-3F-78-42-74-0C-77-E5-45-BB-0B-2A-DE-90-B3-84-F1-19-F6-AB-5
7-B6-80-B7-AA-57-5A-2F-40-93-9F

Accepted answer: (StringToByteArray)
Time to parse 100000 times: 233.1264ms
Result as base64: BMY/eEJ0DHflRbsLKt6Qs4TxGfarV7aAt6pXWi9Ak58=
BitConverter'd: 04-C6-3F-78-42-74-0C-77-E5-45-BB-0B-2A-DE-90-B3-84-F1-19-F6-AB-5
7-B6-80-B7-AA-57-5A-2F-40-93-9F

With Mono's implementation:
Time to parse 100000 times: 777.2544ms
Result as base64: BMY/eEJ0DHflRbsLKt6Qs4TxGfarV7aAt6pXWi9Ak58=
BitConverter'd: 04-C6-3F-78-42-74-0C-77-E5-45-BB-0B-2A-DE-90-B3-84-F1-19-F6-AB-5
7-B6-80-B7-AA-57-5A-2F-40-93-9F

With SoapHexBinary:
Time to parse 100000 times: 845.1456ms
Result as base64: BMY/eEJ0DHflRbsLKt6Qs4TxGfarV7aAt6pXWi9Ak58=
BitConverter'd: 04-C6-3F-78-42-74-0C-77-E5-45-BB-0B-2A-DE-90-B3-84-F1-19-F6-AB-5
7-B6-80-B7-AA-57-5A-2F-40-93-9F

Base64 및 'Bitconver'd'라인은 정확성을 테스트 할 수 있습니다. 그것들은 동일합니다.

구현 :

public static byte[] ToByteArrayFromHex(string hexString)
{
  if (hexString.Length % 2 != 0) throw new ArgumentException("String must have an even length");
  var array = new byte[hexString.Length / 2];
  for (int i = 0; i < hexString.Length; i += 2)
  {
    array[i/2] = ByteFromTwoChars(hexString[i], hexString[i + 1]);
  }
  return array;
}

private static byte ByteFromTwoChars(char p, char p_2)
{
  byte ret;
  if (p <= '9' && p >= '0')
  {
    ret = (byte) ((p - '0') << 4);
  }
  else if (p <= 'f' && p >= 'a')
  {
    ret = (byte) ((p - 'a' + 10) << 4);
  }
  else if (p <= 'F' && p >= 'A')
  {
    ret = (byte) ((p - 'A' + 10) << 4);
  } else throw new ArgumentException("Char is not a hex digit: " + p,"p");

  if (p_2 <= '9' && p_2 >= '0')
  {
    ret |= (byte) ((p_2 - '0'));
  }
  else if (p_2 <= 'f' && p_2 >= 'a')
  {
    ret |= (byte) ((p_2 - 'a' + 10));
  }
  else if (p_2 <= 'F' && p_2 >= 'A')
  {
    ret |= (byte) ((p_2 - 'A' + 10));
  } else throw new ArgumentException("Char is not a hex digit: " + p_2, "p_2");

  return ret;
}

나는 몇 가지 물건을 시도했다 unsafe 그리고 (명확하게 중복 된) 문자로 이동합니다 if 다른 방법에 대한 시퀀스이지만 이것은 가장 빠릅니다.

(나는 이것이 질문의 절반에 대한 대답을 인정한다. 나는 String-> byte [] 변환이 과소 표현되었다고 느꼈고, 바이트 []-> 문자열 각도는 잘 다루는 것처럼 보인다. 따라서이 대답.)

안전한 버전 :

public static class HexHelper
{
    [System.Diagnostics.Contracts.Pure]
    public static string ToHex(this byte[] value)
    {
        if (value == null)
            throw new ArgumentNullException("value");

        const string hexAlphabet = @"0123456789ABCDEF";

        var chars = new char[checked(value.Length * 2)];
        unchecked
        {
            for (int i = 0; i < value.Length; i++)
            {
                chars[i * 2] = hexAlphabet[value[i] >> 4];
                chars[i * 2 + 1] = hexAlphabet[value[i] & 0xF];
            }
        }
        return new string(chars);
    }

    [System.Diagnostics.Contracts.Pure]
    public static byte[] FromHex(this string value)
    {
        if (value == null)
            throw new ArgumentNullException("value");
        if (value.Length % 2 != 0)
            throw new ArgumentException("Hexadecimal value length must be even.", "value");

        unchecked
        {
            byte[] result = new byte[value.Length / 2];
            for (int i = 0; i < result.Length; i++)
            {
                // 0(48) - 9(57) -> 0 - 9
                // A(65) - F(70) -> 10 - 15
                int b = value[i * 2]; // High 4 bits.
                int val = ((b - '0') + ((('9' - b) >> 31) & -7)) << 4;
                b = value[i * 2 + 1]; // Low 4 bits.
                val += (b - '0') + ((('9' - b) >> 31) & -7);
                result[i] = checked((byte)val);
            }
            return result;
        }
    }
}

안전하지 않은 버전 성능을 선호하고 신뢰성을 두려워하지 않는 사람들에게. 약 35% 더 빠른 발 헥 (Tohex)과 10% 더 빠른 hex.

public static class HexUnsafeHelper
{
    [System.Diagnostics.Contracts.Pure]
    public static unsafe string ToHex(this byte[] value)
    {
        if (value == null)
            throw new ArgumentNullException("value");

        const string alphabet = @"0123456789ABCDEF";

        string result = new string(' ', checked(value.Length * 2));
        fixed (char* alphabetPtr = alphabet)
        fixed (char* resultPtr = result)
        {
            char* ptr = resultPtr;
            unchecked
            {
                for (int i = 0; i < value.Length; i++)
                {
                    *ptr++ = *(alphabetPtr + (value[i] >> 4));
                    *ptr++ = *(alphabetPtr + (value[i] & 0xF));
                }
            }
        }
        return result;
    }

    [System.Diagnostics.Contracts.Pure]
    public static unsafe byte[] FromHex(this string value)
    {
        if (value == null)
            throw new ArgumentNullException("value");
        if (value.Length % 2 != 0)
            throw new ArgumentException("Hexadecimal value length must be even.", "value");

        unchecked
        {
            byte[] result = new byte[value.Length / 2];
            fixed (char* valuePtr = value)
            {
                char* valPtr = valuePtr;
                for (int i = 0; i < result.Length; i++)
                {
                    // 0(48) - 9(57) -> 0 - 9
                    // A(65) - F(70) -> 10 - 15
                    int b = *valPtr++; // High 4 bits.
                    int val = ((b - '0') + ((('9' - b) >> 31) & -7)) << 4;
                    b = *valPtr++; // Low 4 bits.
                    val += (b - '0') + ((('9' - b) >> 31) & -7);
                    result[i] = checked((byte)val);
                }
            }
            return result;
        }
    }
}

BTW벤치 마크 테스트 초기 알파벳을 초기화 할 때마다 변환 함수가 잘못되면 알파벳은 const (문자열) 또는 정적 리디 폰 (char [])이어야합니다 (char []). 그런 다음 바이트 기반의 알파벳 기반 []을 문자열로 변환하는 것은 바이트 조작 버전만큼 빠르게됩니다.

물론 테스트는 (최적화와 함께) 릴리스에서 컴파일되어야하며 디버그 옵션 "억제 JIT 최적화"가 꺼져 있어야합니다 (코드가 디버그 가능 해야하는 경우 코드 만 활성화하는 것과 동일).

Waleed Eissa 코드의 역 함수 (Hex String to Byte Array) :

    public static byte[] HexToBytes(this string hexString)        
    {
        byte[] b = new byte[hexString.Length / 2];            
        char c;
        for (int i = 0; i < hexString.Length / 2; i++)
        {
            c = hexString[i * 2];
            b[i] = (byte)((c < 0x40 ? c - 0x30 : (c < 0x47 ? c - 0x37 : c - 0x57)) << 4);
            c = hexString[i * 2 + 1];
            b[i] += (byte)(c < 0x40 ? c - 0x30 : (c < 0x47 ? c - 0x37 : c - 0x57));
        }

        return b;
    }

소문자 지원이있는 waleed eissa 기능 :

    public static string BytesToHex(this byte[] barray, bool toLowerCase = true)
    {
        byte addByte = 0x37;
        if (toLowerCase) addByte = 0x57;
        char[] c = new char[barray.Length * 2];
        byte b;
        for (int i = 0; i < barray.Length; ++i)
        {
            b = ((byte)(barray[i] >> 4));
            c[i * 2] = (char)(b > 9 ? b + addByte : b + 0x30);
            b = ((byte)(barray[i] & 0xF));
            c[i * 2 + 1] = (char)(b > 9 ? b + addByte : b + 0x30);
        }

        return new string(c);
    }

확장 방법 (면책 조항 : 완전히 테스트되지 않은 코드, BTW ...) :

public static class ByteExtensions
{
    public static string ToHexString(this byte[] ba)
    {
        StringBuilder hex = new StringBuilder(ba.Length * 2);

        foreach (byte b in ba)
        {
            hex.AppendFormat("{0:x2}", b);
        }
        return hex.ToString();
    }
}

등 .. 어느 쪽을 사용하십시오 Tomalak의 세 가지 솔루션 (마지막 것은 문자열의 확장 메소드입니다).

Microsoft 개발자로부터, 멋지고 간단한 전환 :

public static string ByteArrayToString(byte[] ba) 
{
    // Concatenate the bytes into one long string
    return ba.Aggregate(new StringBuilder(32),
                            (sb, b) => sb.Append(b.ToString("X2"))
                            ).ToString();
}

위의 내용은 컴팩트 한 반면, 공연 중독자는 열거자를 사용하여 비명을 지 릅니다. Tomolak의 원래 답변을 향상된 버전으로 최고 성능을 얻을 수 있습니다.

public static string ByteArrayToString(byte[] ba)   
{   
   StringBuilder hex = new StringBuilder(ba.Length * 2);   

   for(int i=0; i < ga.Length; i++)       // <-- Use for loop is faster than foreach   
       hex.Append(ba[i].ToString("X2"));   // <-- ToString is faster than AppendFormat   

   return hex.ToString();   
} 

이것은 내가 지금까지 여기에 게시 한 모든 루틴 중 가장 빠릅니다. 내 말을 받아들이지 마십시오 ... 성능을 각 루틴으로 테스트하고 CIL 코드를 직접 검사하십시오.

속도 측면에서 이것은 여기에서 무엇보다 낫습니다.

  public static string ToHexString(byte[] data) {
    byte b;
    int i, j, k;
    int l = data.Length;
    char[] r = new char[l * 2];
    for (i = 0, j = 0; i < l; ++i) {
      b = data[i];
      k = b >> 4;
      r[j++] = (char)(k > 9 ? k + 0x37 : k + 0x30);
      k = b & 15;
      r[j++] = (char)(k > 9 ? k + 0x37 : k + 0x30);
    }
    return new string(r);
  }

나는 당신이 제안한 코드를 얻지 못했습니다, Olipro. hex[i] + hex[i+1] 분명히 반환했다 int.

그러나 나는 Waleeds 코드에서 힌트를 가져 와서 이것을 함께 망치면서 약간의 성공을 거두었습니다. 그것은 지옥처럼 못 생겼지 만 내 테스트 (Patridges 테스트 메커니즘 사용)에 따라 다른 시간의 1/3에 작동하는 것처럼 보입니다. 입력 크기에 따라. 0-9를 먼저 분리하려면? : s를 전환하면 글자보다 숫자가 더 많기 때문에 약간 빠른 결과가 나올 것입니다.

public static byte[] StringToByteArray2(string hex)
{
    byte[] bytes = new byte[hex.Length/2];
    int bl = bytes.Length;
    for (int i = 0; i < bl; ++i)
    {
        bytes[i] = (byte)((hex[2 * i] > 'F' ? hex[2 * i] - 0x57 : hex[2 * i] > '9' ? hex[2 * i] - 0x37 : hex[2 * i] - 0x30) << 4);
        bytes[i] |= (byte)(hex[2 * i + 1] > 'F' ? hex[2 * i + 1] - 0x57 : hex[2 * i + 1] > '9' ? hex[2 * i + 1] - 0x37 : hex[2 * i + 1] - 0x30);
    }
    return bytes;
}

이 버전의 BytearRayToHexViaByTemanipulation이 더 빠를 수 있습니다.

내 보고서에서 :

  • BYTEARRAYTOHEXVIABYTEMANIPULATION3 : 1,68 평균 진드기 (1000 개 이상), 17,5X
  • BYTEARRAYTOHEXVIABYTEMANIPULATION2 : 1,73 평균 진드기 (1000 개 이상), 16,9X
  • BYTEARRAYTOHEXVIABYTEMANIPULATION : 2,90 평균 진드기 (1000 개 이상), 10,1X
  • BYTEARRAYTOHEXVIALOOKUPANDSHIFT : 3,22 평균 진드기 (1000 개 이상), 9,1X
  • ...

    static private readonly char[] hexAlphabet = new char[]
        {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
    static string ByteArrayToHexViaByteManipulation3(byte[] bytes)
    {
        char[] c = new char[bytes.Length * 2];
        byte b;
        for (int i = 0; i < bytes.Length; i++)
        {
            b = ((byte)(bytes[i] >> 4));
            c[i * 2] = hexAlphabet[b];
            b = ((byte)(bytes[i] & 0xF));
            c[i * 2 + 1] = hexAlphabet[b];
        }
        return new string(c);
    }
    

그리고 나는 이것이 최적화라고 생각합니다.

    static private readonly char[] hexAlphabet = new char[]
        {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
    static string ByteArrayToHexViaByteManipulation4(byte[] bytes)
    {
        char[] c = new char[bytes.Length * 2];
        for (int i = 0, ptr = 0; i < bytes.Length; i++, ptr += 2)
        {
            byte b = bytes[i];
            c[ptr] = hexAlphabet[b >> 4];
            c[ptr + 1] = hexAlphabet[b & 0xF];
        }
        return new string(c);
    }

나는 비트 홀딩을 사용하는 대답이 있기 때문에이 비트 피들링 경쟁에 참가할 것입니다. 풀다 16 진수. 캐릭터 배열을 사용하면 StringBuilder 방법도 시간이 걸립니다.

public static String ToHex (byte[] data)
{
    int dataLength = data.Length;
    // pre-create the stringbuilder using the length of the data * 2, precisely enough
    StringBuilder sb = new StringBuilder (dataLength * 2);
    for (int i = 0; i < dataLength; i++) {
        int b = data [i];

        // check using calculation over bits to see if first tuple is a letter
        // isLetter is zero if it is a digit, 1 if it is a letter
        int isLetter = (b >> 7) & ((b >> 6) | (b >> 5)) & 1;

        // calculate the code using a multiplication to make up the difference between
        // a digit character and an alphanumerical character
        int code = '0' + ((b >> 4) & 0xF) + isLetter * ('A' - '9' - 1);
        // now append the result, after casting the code point to a character
        sb.Append ((Char)code);

        // do the same with the lower (less significant) tuple
        isLetter = (b >> 3) & ((b >> 2) | (b >> 1)) & 1;
        code = '0' + (b & 0xF) + isLetter * ('A' - '9' - 1);
        sb.Append ((Char)code);
    }
    return sb.ToString ();
}

public static byte[] FromHex (String hex)
{

    // pre-create the array
    int resultLength = hex.Length / 2;
    byte[] result = new byte[resultLength];
    // set validity = 0 (0 = valid, anything else is not valid)
    int validity = 0;
    int c, isLetter, value, validDigitStruct, validDigit, validLetterStruct, validLetter;
    for (int i = 0, hexOffset = 0; i < resultLength; i++, hexOffset += 2) {
        c = hex [hexOffset];

        // check using calculation over bits to see if first char is a letter
        // isLetter is zero if it is a digit, 1 if it is a letter (upper & lowercase)
        isLetter = (c >> 6) & 1;

        // calculate the tuple value using a multiplication to make up the difference between
        // a digit character and an alphanumerical character
        // minus 1 for the fact that the letters are not zero based
        value = ((c & 0xF) + isLetter * (-1 + 10)) << 4;

        // check validity of all the other bits
        validity |= c >> 7; // changed to >>, maybe not OK, use UInt?

        validDigitStruct = (c & 0x30) ^ 0x30;
        validDigit = ((c & 0x8) >> 3) * (c & 0x6);
        validity |= (isLetter ^ 1) * (validDigitStruct | validDigit);

        validLetterStruct = c & 0x18;
        validLetter = (((c - 1) & 0x4) >> 2) * ((c - 1) & 0x2);
        validity |= isLetter * (validLetterStruct | validLetter);

        // do the same with the lower (less significant) tuple
        c = hex [hexOffset + 1];
        isLetter = (c >> 6) & 1;
        value ^= (c & 0xF) + isLetter * (-1 + 10);
        result [i] = (byte)value;

        // check validity of all the other bits
        validity |= c >> 7; // changed to >>, maybe not OK, use UInt?

        validDigitStruct = (c & 0x30) ^ 0x30;
        validDigit = ((c & 0x8) >> 3) * (c & 0x6);
        validity |= (isLetter ^ 1) * (validDigitStruct | validDigit);

        validLetterStruct = c & 0x18;
        validLetter = (((c - 1) & 0x4) >> 2) * ((c - 1) & 0x2);
        validity |= isLetter * (validLetterStruct | validLetter);
    }

    if (validity != 0) {
        throw new ArgumentException ("Hexadecimal encoding incorrect for input " + hex);
    }

    return result;
}

Java 코드에서 변환.

을 위한 성능을 나와 함께 가 drphrozens 솔루션입니다.작은 최적화를 위한 디코더가 될 수 있을 사용에 대한 테이블 하나 char 을 제거하"<< 4"입니다.

분명히 두 가지 메소드를 호출은 비용이 많이 듭니다.는 경우 몇 가지 종류의 체크인은 하나에 입력 또는 출력 데이터(수 있습 CRC checksum 또 무엇이든)의 if (b == 255)... 수 건너뛰고함으로써 또한 메소드를 호출합니다.

offset++offsetoffsetoffset + 1 을 줄 수 있는 몇 가지 이론적 이익이지만 나는 컴파일러 손잡이 이 나보다 낫다.

private static readonly byte[] LookupTableLow = new byte[] {
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};

private static readonly byte[] LookupTableHigh = new byte[] {
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};

private static byte LookupLow(char c)
{
  var b = LookupTableLow[c];
  if (b == 255)
    throw new IOException("Expected a hex character, got " + c);
  return b;
}

private static byte LookupHigh(char c)
{
  var b = LookupTableHigh[c];
  if (b == 255)
    throw new IOException("Expected a hex character, got " + c);
  return b;
}

public static byte ToByte(char[] chars, int offset)
{
  return (byte)(LookupHigh(chars[offset++]) | LookupLow(chars[offset]));
}

이것은 단지 상단의 머리되지 않았고 테스트하거나 벤치마킹합니다.

SQL 문자열에 삽입하려면 (명령 매개 변수를 사용하지 않는 경우) :

public static String ByteArrayToSQLHexString(byte[] Source)
{
    return = "0x" + BitConverter.ToString(Source).Replace("-", "");
}

다양성의 또 다른 변형 :

public static byte[] FromHexString(string src)
{
    if (String.IsNullOrEmpty(src))
        return null;

    int index = src.Length;
    int sz = index / 2;
    if (sz <= 0)
        return null;

    byte[] rc = new byte[sz];

    while (--sz >= 0)
    {
        char lo = src[--index];
        char hi = src[--index];

        rc[sz] = (byte)(
            (
                (hi >= '0' && hi <= '9') ? hi - '0' :
                (hi >= 'a' && hi <= 'f') ? hi - 'a' + 10 :
                (hi >= 'A' && hi <= 'F') ? hi - 'A' + 10 :
                0
            )
            << 4 | 
            (
                (lo >= '0' && lo <= '9') ? lo - '0' :
                (lo >= 'a' && lo <= 'f') ? lo - 'a' + 10 :
                (lo >= 'A' && lo <= 'F') ? lo - 'A' + 10 :
                0
            )
        );
    }

    return rc;          
}

속도로 최적화되지 않았지만 대부분의 답변보다 더 많은 linqy (.net 4.0) :

<Extension()>
Public Function FromHexToByteArray(hex As String) As Byte()
    hex = If(hex, String.Empty)
    If hex.Length Mod 2 = 1 Then hex = "0" & hex
    Return Enumerable.Range(0, hex.Length \ 2).Select(Function(i) Convert.ToByte(hex.Substring(i * 2, 2), 16)).ToArray
End Function

<Extension()>
Public Function ToHexString(bytes As IEnumerable(Of Byte)) As String
    Return String.Concat(bytes.Select(Function(b) b.ToString("X2")))
End Function

두 개의 니블 작업을 하나로 접는 두 개의 매시업.

아마도 매우 효율적인 버전 :

public static string ByteArrayToString2(byte[] ba)
{
    char[] c = new char[ba.Length * 2];
    for( int i = 0; i < ba.Length * 2; ++i)
    {
        byte b = (byte)((ba[i>>1] >> 4*((i&1)^1)) & 0xF);
        c[i] = (char)(55 + b + (((b-10)>>31)&-7));
    }
    return new string( c );
}

퇴폐적 인 LINQ-with-Bit-Hacking 버전 :

public static string ByteArrayToString(byte[] ba)
{
    return string.Concat( ba.SelectMany( b => new int[] { b >> 4, b & 0xF }).Select( b => (char)(55 + b + (((b-10)>>31)&-7))) );
}

그리고 반대 :

public static byte[] HexStringToByteArray( string s )
{
    byte[] ab = new byte[s.Length>>1];
    for( int i = 0; i < s.Length; i++ )
    {
        int b = s[i];
        b = (b - '0') + ((('9' - b)>>31)&-7);
        ab[i>>1] |= (byte)(b << 4*((i&1)^1));
    }
    return ab;
}

또 다른 방법은 사용하는 것입니다 stackalloc GC 메모리 압력을 줄이기 위해 :

static string ByteToHexBitFiddle(byte[] bytes)
{
        var c = stackalloc char[bytes.Length * 2 + 1];
        int b; 
        for (int i = 0; i < bytes.Length; ++i)
        {
            b = bytes[i] >> 4;
            c[i * 2] = (char)(55 + b + (((b - 10) >> 31) & -7));
            b = bytes[i] & 0xF;
            c[i * 2 + 1] = (char)(55 + b + (((b - 10) >> 31) & -7));
        }
        c[bytes.Length * 2 ] = '\0';
        return new string(c);
}

여기 내 샷이 있습니다. 스트링과 바이트를 확장하기 위해 한 쌍의 확장 클래스를 만들었습니다. 대형 파일 테스트에서 성능은 바이트 조작 2와 비슷합니다.

TohexString의 아래 코드는 조회 및 시프트 알고리즘의 최적화 된 구현입니다. 그것은 Behrooz의 것과 거의 동일하지만 사용하는 것이 밝혀졌습니다. foreach 반복하고 카운터가 명시 적으로 인덱싱하는 것보다 빠릅니다. for.

내 컴퓨터에서 바이트 조작 2 뒤에 2 위를 차지하며 매우 읽기 쉬운 코드입니다. 다음 테스트 결과도 관심이 있습니다.

ToHexStringcharArraywithcharrayLookup : 41,589.69 평균 진드기 (1000 개 이상), 1.5 배의 ToHexStringChararraywithStringLookup : 50,764.06 평균 진드기 (1000 개 이상), 1.2 배의 ToHexStringstringBuilderWithcharraylookup : 62,812. 87 the 평균 런

위의 결과를 바탕으로 다음과 같은 결론을 내리는 것이 안전 해 보입니다.

  1. 조회와 char 배열을 수행하기 위해 문자열로 인덱싱하는 처벌은 큰 파일 테스트에서 중요합니다.
  2. 알려진 용량의 스트링 빌더와 알려진 크기의 숯 배열을 사용하여 문자열을 생성하는 처벌은 훨씬 더 중요합니다.

코드는 다음과 같습니다.

using System;

namespace ConversionExtensions
{
    public static class ByteArrayExtensions
    {
        private readonly static char[] digits = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };

        public static string ToHexString(this byte[] bytes)
        {
            char[] hex = new char[bytes.Length * 2];
            int index = 0;

            foreach (byte b in bytes)
            {
                hex[index++] = digits[b >> 4];
                hex[index++] = digits[b & 0x0F];
            }

            return new string(hex);
        }
    }
}


using System;
using System.IO;

namespace ConversionExtensions
{
    public static class StringExtensions
    {
        public static byte[] ToBytes(this string hexString)
        {
            if (!string.IsNullOrEmpty(hexString) && hexString.Length % 2 != 0)
            {
                throw new FormatException("Hexadecimal string must not be empty and must contain an even number of digits to be valid.");
            }

            hexString = hexString.ToUpperInvariant();
            byte[] data = new byte[hexString.Length / 2];

            for (int index = 0; index < hexString.Length; index += 2)
            {
                int highDigitValue = hexString[index] <= '9' ? hexString[index] - '0' : hexString[index] - 'A' + 10;
                int lowDigitValue = hexString[index + 1] <= '9' ? hexString[index + 1] - '0' : hexString[index + 1] - 'A' + 10;

                if (highDigitValue < 0 || lowDigitValue < 0 || highDigitValue > 15 || lowDigitValue > 15)
                {
                    throw new FormatException("An invalid digit was encountered. Valid hexadecimal digits are 0-9 and A-F.");
                }
                else
                {
                    byte value = (byte)((highDigitValue << 4) | (lowDigitValue & 0x0F));
                    data[index / 2] = value;
                }
            }

            return data;
        }
    }
}

다음은 내 코드를 컴퓨터에 @Patridge의 테스트 프로젝트에 넣을 때 얻은 테스트 결과입니다. 또한 16 진수에서 바이트 어레이로 변환하기위한 테스트를 추가했습니다. 내 코드를 행사 한 테스트 실행은 BytearRayToHexViaOptimizedLookUpandshift 및 HextObyteArrayviaBiAtemanipulation입니다. hextobytearrayviaconverttobyte는 XXXX에서 가져 왔습니다. hextobytearrayviasoaphexbinary는 @mykroft의 답변에서 나온 것입니다.

Intel Pentium III Xeon 프로세서

    Cores: 4 <br/>
    Current Clock Speed: 1576 <br/>
    Max Clock Speed: 3092 <br/>

바이트 배열을 16 진 문자열 표현으로 변환합니다


BYTEARRAYTOHEXVIABYTEMANIPULATION2 : 39,366.64 평균 진드기 (1000 개 이상), 22.4x

BYTEARRAYTOHEXVIAOPTIMIZEDLOOKUPANDSHIFT : 41,588.64 평균 진드기 (1000 개 이상), 21.2x

BytearraytoHexVialookup : 55,509.56 평균 진드기 (1000 개 이상), 15.9x

BYTEARRAYTOHEXVIABYTEMANIPULATION : 65,349.12 평균 진드기 (1000 개 이상), 13.5x

BytearraytoHexvialookUpandshift : 86,926.87 평균 진드기 (1000 개 이상), 10.2x

BYTEARRAYTOHEXSTRINGVIABITCONVERTER : 139,353.73 평균 진드기 (1000 개 이상), 6.3x

BYTEARRAYTOHEXVIASOAPHEXBINGING : 314,598.77 평균 진드기 (1000 개 이상), 2.8x

BytearRayToHexStringViastringBuilderForeachByTeToString : 344,264.63 평균 진드기 (1000 개 이상), 2.6x

BytearRayToHexStringViastringBuilderaggegateByTeToString : 382,623.44 평균 진드기 (1000 개 이상), 2.3x

BYTEARRAYTOHEXSTRINGTRINGTRINGBUILDERFORECHAPPENDFORMAT : 818,111.95 평균 진드기 (1000 개 이상), 1.1x

BytearRayToHexStringViastringConcatarrayConvertAll : 839,244.84 평균 진드기 (1000 개 이상), 1.1x

BYTEARRAYTOHEXSTRINGTRINGTRINGBUILDERAGATEAPPENDFORNFENDFORNAT : 867,303.98 평균 진드기 (1000 개 이상), 1.0x

BYTEARRAYTOHEXSTRINGTRINGTRINGJOINARRAYCONVERTALL : 882,710.28 평균 진드기 (1000 개 이상), 1.0x


또 다른 빠른 기능 ...

private static readonly byte[] HexNibble = new byte[] {
    0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
    0x8, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    0x0, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x0,
    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    0x0, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF
};

public static byte[] HexStringToByteArray( string str )
{
    int byteCount = str.Length >> 1;
    byte[] result = new byte[byteCount + (str.Length & 1)];
    for( int i = 0; i < byteCount; i++ )
        result[i] = (byte) (HexNibble[str[i << 1] - 48] << 4 | HexNibble[str[(i << 1) + 1] - 48]);
    if( (str.Length & 1) != 0 )
        result[byteCount] = (byte) HexNibble[str[str.Length - 1] - 48];
    return result;
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top