string hex to array byte linq c# 바이트 배열을 16 진수 문자열로 변환하는 방법은 무엇입니까?




15 Answers

성능 분석

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

Stopwatch 성능 테스트, 무작위 문장 (n = 61, 1000 반복) 및 Project Gutenburg 텍스트 (n = 1,238,957, 150 반복)를 사용한 실행을 통해 다양한 변환 방법을 각각 실행했습니다. 대략적으로 가장 빠른 것부터 가장 느린 것까지 결과가 있습니다. 모든 측정 값은 틱 단위 ( 10,000 tick = 1ms )이며 모든 관련 노트는 [가장 느린] StringBuilder 구현과 비교됩니다. 사용 된 코드에 대해서는 아래를 참조하거나 테스트 프레임 워크 레포 에서이 코드를 실행해야합니다.

기권

경고 : 콘크리트에 대해서는이 통계에 의존하지 마십시오. 그들은 단순히 샘플 데이터의 샘플 실행입니다. 최고 수준의 성능이 정말로 필요하면 생산 방법을 나타내는 환경에서 이러한 방법을 테스트하여 사용하려는 데이터를 나타내는 데이터를 작성하십시오.

결과

  • CodesInChaos를 통해 unsafe 바이트로 검색 (airbreather의 테스트 레포에 airbreather )
    • 텍스트 : 4,727.85 (105.2X)
    • 문장 : 0.28 (99.7X)
  • 바이트 별 조회 (CodesInChaos를 통해)
    • 텍스트 : 10,853.96 (45.8X 빨라짐)
    • 문장 : 0.65 (42.7X 빨라짐)
  • 바이트 조작 2 (CodesInChaos 경유)
    • 텍스트 : 12,967.69 (38.4X 빨라짐)
    • 문장 : 0.73 (37.9X 빨라짐)
  • 바이트 조작 (Waleed Eissa를 통해)
    • 텍스트 : 16,856.64 (29.5X 빨라짐)
    • 문장 : 0.70 (39.5X 빨라짐)
  • 조회 / 시프트 (Nathan Moinvaziri를 통해)
    • 텍스트 : 23,201.23 (21.4X 빨라짐)
    • 문장 : 1.24 (22.3X 빨라짐)
  • 니블 조회 (브라이언 램버트 경유)
    • 텍스트 : 23,879.41 (20.8X 빨라짐)
    • 문장 : 1.15 (23.9X 빨라짐)
  • BitConverter (Tomalak을 통해)
    • 텍스트 : 113,269.34 (4.4X 빨라짐)
    • 문장 : 9.98 (2.8X 빨라짐)
  • {SoapHexBinary}.ToString (Mykroft를 통해)
    • 텍스트 : 178,601.39 (2.8 배 빨라짐)
    • 문장 : 10.68 (2.6X 빨라짐)
  • {byte}.ToString("X2") ( foreach 사용 {byte}.ToString("X2") ( {byte}.ToString("X2") Dean의 답변에서 파생 됨)
    • 텍스트 : 308,805.38 (2.4X 빨라짐)
    • 문장 : 16.89 (2.4X 빠름)
  • {byte}.ToString("X2") ( {IEnumerable}.Aggregate , System.Linq 필요) (Mark를 통해)
    • 텍스트 : 352,828.20 (2.1 배 빨라짐)
    • 문장 : 16.87 (2.4X 빠름)
  • Array.ConvertAll ( Array.ConvertAll 사용) (Will Dean을 통해)
    • 텍스트 : 675,451.57 (1.1 배 빨라짐)
    • 문장 : 17.95 (2.2X 빨라짐)
  • Array.ConvertAll ( string.Concat 사용, .NET 4.0 필요) (Will Dean을 통해)
    • 텍스트 : 752,078.70 (1.0X 빨라짐)
    • 문장 : 18.28 (2.2X 빨라짐)
  • {StringBuilder}.AppendFormat ( foreach 사용) (Tomalak을 통해)
    • 텍스트 : 672,115.77 (1.1X 빨라짐)
    • 문장 : 36.82 (1.1 배 빨라짐)
  • {StringBuilder}.AppendFormat ( {IEnumerable}.Aggregate {StringBuilder}.AppendFormat 사용, System.Linq 필요) (Tomalak의 답변에서 파생 됨)
    • 텍스트 : 718,380.63 (1.0X 빨라짐)
    • 문장 : 39.71 (1.0X 빨라짐)

조회 테이블은 바이트 조작보다 앞서갔습니다. 기본적으로 어떤 주어진 니블이나 바이트가 16 진수 일지를 미리 계산하는 형식이 있습니다. 그런 다음 데이터를 훑어 보면 다음 부분을 조회하여 16 진수 문자열이 무엇인지 확인할 수 있습니다. 그런 다음 그 값은 어떤 방식으로 결과 문자열 출력에 추가됩니다. 오랫동안 바이트 조작을 위해 일부 개발자는 읽을 수 없도록하는 것이 가장 성과가 좋은 방법이었습니다.

최선의 방법은 여전히 ​​일부 대표 데이터를 찾아 생산 환경과 같은 방식으로 시도하는 것입니다. 다른 메모리 제약 조건이있는 경우 할당 속도가 더 빠르지 만 더 많은 메모리를 소비하는 할당보다 적은 방법을 선호 할 수 있습니다.

테스트 코드

내가 사용한 테스트 코드로 자유롭게 플레이하십시오. 여기에 버전이 포함되어 있지만 repo 를 복제하고 직접 방법을 추가하십시오. 흥미로운 것을 찾거나 사용하는 테스트 프레임 워크를 개선하려는 경우 pull 요청을 제출하십시오.

  1. /Tests/ConvertByteArrayToHexString/Test.cs에 새 정적 메서드 ( Func<byte[], string> )를 추가합니다.
  2. 해당 클래스의 TestCandidates 반환 값에 해당 메서드의 이름을 추가합니다.
  3. 동일한 클래스의 GenerateTestInput 에있는 주석을 토글하여 원하는 입력 버전 (문장 또는 텍스트)을 실행하는지 확인하십시오.
  4. 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();
}

업데이트 (2010-01-13)

분석에 대한 Waleed의 대답을 추가했습니다. 아주 빠릅니다.

업데이트 (2011-10-05)

완성을 위해 string.Concat Array.ConvertAll 변형을 추가했습니다 (.NET 4.0 필요). string.Joinstring.Join 합니다. string.Join 버전.

업데이트 (2012-02-05)

테스트 레포에는 StringBuilder.Append(b.ToString("X2")) 와 같은 다양한 변형이 포함됩니다. 결과를 전혀 뒤엎 지 않습니다. foreach{IEnumerable}.Aggregate BitConverter 보다 빠르지 만 BitConverter 여전히 승리합니다.

업데이트 (2012-04-03)

3 위를 차지한 Mykroft의 SoapHexBinary 대답을 분석에 추가했습니다.

업데이트 (2013-01-15)

CodesInChaos의 바이트 조작 대답을 추가하여 첫 번째 자리를 차지했습니다 (큰 텍스트 블록에 큰 차이가 있음).

업데이트 (2013-05-23)

Brian Lambert의 블로그에서 Nathan Moinvaziri의 조회 답변과 변형을 추가했습니다. 둘 다 오히려 빠르지 만 필자가 사용했던 테스트 머신에서 선두 자리를 지키지 않았습니다 (AMD Phenom 9750).

업데이트 (2014-07-31)

@ CodesInChaos의 새로운 바이트 기반 조회 대답을 추가했습니다. 문장 테스트와 전문 텍스트 테스트 모두에서 주도권을 잡은 것으로 보인다.

업데이트 (2015-08-20)

답변의 repo에 airbreather 최적화 및 unsafe 변형이 추가되었습니다. 안전하지 않은 게임에서 게임을하고 싶다면 짧은 문자열과 큰 텍스트 모두에서 이전 우승자보다 큰 성능 향상을 얻을 수 있습니다.

bytes to hex c#

어떻게 바이트 배열을 16 진수 문자열로 변환 할 수 있습니까?




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

꽤 빠릅니다.

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
    b < 10 경우 < 0 이며 십진수가됩니다.
    값이 b > 10 경우 >= 0 이며 A 에서 F 까지 A 문자가됩니다.
  3. 부호가있는 32 비트 정수에 i >> 31 을 사용하면 부호 확장으로 인해 부호가 추출됩니다. i >= 0 i < 0-1 이되고 i >= 0 됩니다.
  4. 2)와 3)을 결합하면 (b-10)>>31 은 문자의 경우 0 , 숫자의 경우 -1 이됩니다.
  5. 이 경우를 보면, 마지막 summand는 0 되고, b 는 10-15의 범위에 있습니다. 우리는 A (65)에서 F (70)로 매핑하려고합니다. 이것은 55를 추가하는 것을 의미합니다 ( 'A'-10 ) .
  6. 숫자의 경우를 살펴보면 마지막 summand를 적용하여 b 를 0에서 9까지의 범위를 0 (48)에서 9 (57)까지 매핑합니다. 이것은 -7이되어야 함을 의미합니다 ( '0' - 55 0'- '0' - 55 ).
    이제 -1을 곱하면됩니다. 그러나 -1은 모든 비트가 1로 표시되기 때문에 (0 & -7) == 0 이고 (-1 & -7) == -7 이므로 & -7 대신 사용할 수 있습니다.

몇 가지 추가 고려 사항 :

  • 나는 두 번째 루프 변수를 사용하여 색인을 작성하지 않았으므로 측정 값이 i 에서 계산하는 것이 더 저렴하다는 것을 보여줍니다.
  • 정확하게 i < bytes.Length 를 루프의 상한으로 사용하면 JITter는 bytes[i] 에 대한 경계 검사를 제거 할 수 있으므로 해당 변형을 선택했습니다.
  • int를 b 로하면 불필요한 바이트 간 변환이 가능합니다.



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

출처 : Forum post byte [] Hex String에 대한 배열 (PZahra의 게시물 참조). 0x 프리픽스를 제거하기 위해 코드를 조금 수정했습니다.

성능 테스트를 코드에 수행 한 결과, BitConverter.ToString () (patridge의 게시물에 따르면 가장 빠른 속도)을 사용하는 것보다 거의 8 배 빠릅니다.




이것은 share (및 후속 수정 사항) share 개정판 4 에 share 입니다.

이 편집이 잘못되었다는 사실을 알리고 왜 되돌릴 수 있는지 설명합니다. 길을 따라 몇 가지 내부 구조에 대해 한 두 가지를 배울 수 있으며 조기 최적화가 실제로 무엇인지, 그리고 어떻게 조만간 당신을 물릴 수 있는지에 대한 또 다른 예를 볼 수 있습니다.

tl; dr : Convert.ToByteString.Substring 사용하면 바쁘다 (아래 "원본 코드"). Convert.ToByte 를 다시 구현하지 않으려는 경우 가장 좋은 조합입니다. 성능이 필요한 경우 Convert.ToByte 사용하지 않는 고급 기능을 사용하십시오 (다른 답변 참조). Convert.ToByte 와 함께 String.Substring 이외의 다른 것을 사용하지 마십시오. String.Substring 답변에 대한 의견에서이 점에 대해 흥미로운 점이 없으면 말입니다.

경고 : 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 피하고 대신 StringReader 사용합니다. 주어진 이유는 다음과 같습니다.

편집 : 다음과 같이 단일 패스 구문 분석기를 사용하여 긴 문자열의 성능을 향상시킬 수 있습니다.

음, String.Substring 에 대한 참조 코드를 보면 분명히 이미 "싱글 패스"입니다. 왜 그렇게해서는 안되나요? 바이트 쌍은 surrogate 쌍이 아닌 바이트 수준에서 작동합니다.

그러나 새 문자열을 할당하지만 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 진수는 두 자리 (기호)를 사용하는 단일 옥텟을 나타냅니다.

그런데 왜 StringReader.Read 두 번 호출합니까? 두 번째 오버로드를 호출하고 한 번에 2 문자 배열의 두 문자를 읽도록 요청하십시오. 통화량을 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;
    }

당신이 남긴 것은 문자열 판독기로, "값"은 _pos자신을 선언 할 수 있는 병렬 색인 (내부 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두 단계의 단계로 증가 하는 지수가 필요 i합니까? 물론 i2 개를 곱하면 됩니다 (컴파일러가 추가로 최적화 할 수 있어야합니다).

    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문자열을 할당하고 그것에 데이터를 복사, 다음, 자신을 문자열을 할당하고 데이터를 복사 할 수는 중간 배열을 사용하고있는 당신은 진수 숫자를 복사 다시 에서 배열 및 문자열로 (문자열 생성자에서 전달할 때). 문자열이 이미 인턴 풀에 있으면 두 번째 복사본이 최적화 될 수 있지만 이러한 경우에는 문자열 String.Substring을 피할 수 있습니다.

사실, String.Substring다시 살펴보면 문자열이 생성되는 방식에 대한 내부 지식을 사용하여 문자열을 일반적으로 할 수있는 것보다 빠르게 할당하는 것을 볼 수 있습니다. 그리고 CopyTo직접적으로 거기에서 사용 된 것과 동일한 코드를 사용 하여 피할 수 있습니다 호출 오버 헤드.

String.Substring

  • 최악의 경우 : 하나의 빠른 할당, 하나의 빠른 복사.
  • 우수 사례 : 할당, 복사 안 함.

수동 방법

  • 최악의 경우 : 두 개의 일반 할당, 한 개의 일반 복사, 한 개의 빠른 복사.
  • 우수 사례 : 하나의 정상 할당, 하나의 정상 복사.

결론? Convert.ToByte(String, Int32)이 기능을 직접 구현하고 싶지 않기 때문에 사용 하고 싶다면 이길 방법이없는 것 같습니다 String.Substring. 당신이하는 모든 일은 서클에서 돌아가고, 휠을 다시 발명합니다 (차선의 재료만으로).

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

a가있는 경우 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

재미있어! (하지만 검토를 통해 최적화하십시오.)




이것은 훌륭한 글입니다. 나는 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;
}



여기에 많은 답을 쌓아 두지는 않겠지 만, 헥스 문자열 파서를 직접 구현하는 것 (~ 4.5x보다 좋음)을 알게되었습니다. 첫째, 내 테스트 결과 (첫 번째 배치는 내 구현 임) :

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 및 'BitConverter'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시퀀스를 다른 방법으로 이동하지만, 이것이 가장 빠르다.

(나는 이것이 절반의 질문에 답하는 것을 인정한다. 문자열 -> 바이트 [] 변환이 과소 표현 된 반면, 바이트 [] -> 문자열 각도는 잘 덮여있는 것 같다.




Waleed Eissa 코드에 대한 역함수 (16 진수 문자열에서 바이트 배열로) :

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



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 코드를 스스로 점검하라.




네가 제안한 코드를 얻지 못했어, 올리 프로 hex[i] + hex[i+1]외관상으로는 돌려 보냈다 int.

나는 그러나 Waleeds 코드에서 힌트를 취해 이것을 함께 망치는 것으로 성공을 거두었습니다. 지옥처럼 추악하지만 내 테스트 (patches 테스팅 메커니즘 사용)에 따라 다른 것보다 시간당 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;
}



나는 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 코드에서 변환되었습니다.




다양성을위한 또 다른 변형 :

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



두 개의 nibble 연산을 하나로 접는 두 개의 매시업.

아마 꽤 효율적인 버전 :

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



내 사기가있다. 문자열과 바이트를 확장하는 확장 클래스 쌍을 만들었습니다. 대용량 파일 테스트에서 성능은 Byte 조작 2와 유사합니다.

ToHexString의 아래 코드는 조회 및 이동 알고리즘의 최적화 된 구현입니다. 그것은 베루스 (Behrooz)의 것과 거의 동일하지만 foreach반복을 위해 a 를 사용하는 것으로 밝혀졌습니다. 그리고 카운터는 명시 적 인덱싱보다 빠릅니다 for.

그것은 내 컴퓨터에서 바이트 조작 2 뒤에 2 위를 차지하며 매우 읽기 쉬운 코드입니다. 다음 테스트 결과도 중요합니다.

ToHexStringCharArrayWithCharArrayLookup : 41,589.69 평균 틱 (1000 실행), 1.5 배 ToHexStringCharArrayWithStringLookup : 50,764.06 평균 틱 (1000 실행), 1.2 배 ToHexStringStringBuilderWithCharArrayLookup : 62,812.87 평균 틱 (1000 실행), 1.0X

위의 결과를 바탕으로 다음과 같은 결론을 내리는 것이 안전하다고 판단됩니다.

  1. 조회를 수행하기위한 문자열로의 인덱싱에 대한 벌칙은 대용량 파일 테스트에서 중요합니다.
  2. 알려진 용량의 StringBuilder 대 문자열을 생성하는 알려진 크기의 char 배열을 사용하는 것에 대한 벌칙은 훨씬 더 중요합니다.

코드는 다음과 같습니다.

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 및 HexToByteArrayViaByteManipulation입니다. 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

ByteArrayToHexViaSoapHexBinary : 314,598.77 평균 틱 (1000 회 이상 실행), 2.8X

ByteArrayToHexStringViaStringBuilderForEachByteToString : 344,264.63 평균 틱 수 (1000 회 이상), 2.6X

ByteArrayToHexStringViaStringBuilderAggregateByteToString : 382,623.44 평균 틱 수 (1000 회 이상), 2.3X

ByteArrayToHexStringViaStringBuilderForEachAppendFormat : 818,111.95 평균 틱 (1000 회 이상 실행), 1.1X

ByteArrayToHexStringViaStringConcatArrayConvertAll : 839,244.84 평균 틱 (1000 회 이상 실행), 1.1X

ByteArrayToHexStringViaStringBuilderAggregateAppendFormat : 867,303.98 평균 틱 (1000 회 이상 실행), 1.0X

ByteArrayToHexStringViaStringJoinArrayConvertAll : 882,710.28 평균 틱 (1000 회 이상 실행), 1.0X




성능을 위해 drphrozens 솔루션을 사용합니다. 디코더에 대한 작은 최적화는 "<< 4"를 없애기 위해 char을위한 테이블을 사용할 수 있습니다.

두 메서드 호출은 비용이 많이 듭니다. 입력 또는 출력 데이터 (CRC, 체크섬 등)에서 어떤 종류의 검사가 수행 if (b == 255)...되면 건너 뛸 수 있으므로 메서드가 모두 호출합니다.

사용 offset++하고 offset대신 offset하고 offset + 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]));
}

이것은 내 머리 꼭대기에서 떨어져서 테스트되거나 벤치마킹되지 않았습니다.




Related


Tags

c#   arrays   hex