Bir bayt dizisini onaltılık bir dizeye veya tam tersini nasıl dönüştürebilirsiniz?
Bir bayt dizisini onaltılık bir dizeye veya tam tersini nasıl dönüştürebilirsiniz?
Yanıtlar:
Ya:
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();
}
veya:
public static string ByteArrayToString(byte[] ba)
{
return BitConverter.ToString(ba).Replace("-","");
}
Bunu yapmanın daha fazla çeşidi vardır, örneğin burada .
Ters dönüşüm şöyle olur:
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;
}
İle birlikte kullanmak Substring
en iyi seçenektir Convert.ToByte
. Daha fazla bilgi için bu cevaba bakınız . Daha iyi performansa ihtiyacınız varsa, Convert.ToByte
düşmeden önce kaçınmalısınız SubString
.
Not: 2015-08-20 itibariyle yeni lider.
Çeşitli dönüşüm yöntemlerinin her birini bir kaba Stopwatch
performans testi, rastgele bir cümleyle (n = 61, 1000 yineleme) ve bir Project Gutenburg metniyle (n = 1,238,957, 150 yineleme) çalıştırdım. İşte sonuçlar, kabaca en hızlıdan en yavaşına. Tüm ölçümler kenelerdedir ( 10.000 keneler = 1 ms ) ve tüm göreli notlar [en yavaş] StringBuilder
uygulama ile karşılaştırılır. Kullanılan kod için, aşağıya bakın veya şimdi bunu çalıştırmak için kodunu koruduğum test çerçevesi repo .
UYARI: Somut bir şey için bu istatistiklere güvenmeyin; bunlar sadece örnek verilerin örnek bir çalışmasıdır. Gerçekten birinci sınıf performansa ihtiyacınız varsa, lütfen bu yöntemleri üretim gereksinimlerinizi temsil eden bir ortamda kullanacağınız veri temsilcisi ile test edin.
unsafe
araması (CodesInChaos üzerinden) (test havasına hava üfleyici tarafından eklendi )
BitConverter
(Tomalak aracılığıyla)
{SoapHexBinary}.ToString
(Mykroft üzerinden)
{byte}.ToString("X2")
(kullanarak foreach
) (Will Dean'in cevabından türetilmiştir)
{byte}.ToString("X2")
(kullanma {IEnumerable}.Aggregate
, System.Linq gerektirir) (Mark aracılığıyla)
Array.ConvertAll
(kullanarak string.Join
) (Will Dean aracılığıyla)
Array.ConvertAll
(kullanma string.Concat
, .NET 4.0 gerektirir) (Will Dean aracılığıyla)
{StringBuilder}.AppendFormat
(kullanarak foreach
) (Tomalak aracılığıyla)
{StringBuilder}.AppendFormat
(kullanma {IEnumerable}.Aggregate
, System.Linq gerektirir) (Tomalak'ın cevabından türetilmiştir)
Arama tabloları, bayt manipülasyonunda öncülük etti. Temel olarak, herhangi bir nibble veya baytın onaltılık olarak ne olacağını önceden hesaplamanın bir biçimi vardır. Sonra, verileri kopyalarken, hangi hex dizesinin olacağını görmek için bir sonraki bölüme bakmanız yeterlidir. Bu değer daha sonra elde edilen dize çıktısına bir şekilde eklenir. Uzun zamandır bazı geliştiriciler tarafından okunması daha zor olan bayt manipülasyonu, en iyi performans gösteren yaklaşımdı.
En iyi bahsiniz hala bazı temsili veriler bulmak ve bunu üretim benzeri bir ortamda denemek olacak. Farklı bellek kısıtlamalarınız varsa, daha hızlı ancak daha fazla bellek tüketen bir taneye daha az tahsis edilen bir yöntemi tercih edebilirsiniz.
Kullandığım test koduyla oynamaktan çekinmeyin. Bir versiyon buraya dahil ancak klonlamak için çekinmeyin olan repo ve kendi yöntemlerini ekleyin. İlginç bir şey bulursanız veya kullandığı test çerçevesinin iyileştirilmesine yardımcı olmak istiyorsanız lütfen bir çekme isteği gönderin.
Func<byte[], string>
) /Tests/ConvertByteArrayToHexString/Test.cs dosyasına ekleyin.TestCandidates
, aynı sınıftaki dönüş değerine ekleyin .GenerateTestInput
Aynı sınıftaki yorumları değiştirerek, istediğiniz cümle veya metin giriş sürümünü çalıştırdığınızdan emin olun .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();
}
Waleed'in analize cevabı eklendi. Oldukça hızlı.
string.Concat
Array.ConvertAll
Tamlık için varyant eklendi (.NET 4.0 gerektirir). string.Join
Sürümü ile eşit .
Test repou gibi daha fazla varyant içerir StringBuilder.Append(b.ToString("X2"))
. Hiçbiri sonuçları bozmaz. örneğin foreach
daha hızlıdır {IEnumerable}.Aggregate
, ancak BitConverter
yine de kazanır.
Mykroft'un SoapHexBinary
üçüncü sırada yer alan analize yanıtı eklendi .
Kodlar İnChaos'un ilk sırada yer alan bayt manipülasyon cevabı (büyük metin bloklarında büyük bir farkla).
Nathan Moinvaziri'nin arama yanıtı ve Brian Lambert'ın blogundaki varyant eklendi. Her ikisi de oldukça hızlı, ancak kullandığım test makinesine liderlik etmiyor (AMD Phenom 9750).
@ CodesInChaos'un yeni bayt tabanlı arama yanıtı eklendi. Hem cümle sınavlarına hem de tam metin sınavlarına öncülük etmiş gibi görünüyor.
Bu yanıtın deposuna airbreather'ın optimizasyonları ve unsafe
varyantı eklendi . Güvensiz oyunda oynamak istiyorsanız, hem kısa iplerde hem de büyük metinlerde önceki en iyi kazananlardan herhangi birinde büyük performans kazanımları elde edebilirsiniz.
bytes.ToHexStringAtLudicrousSpeed()
.).
SoapHexBinary adında tam olarak istediğinizi yapan bir sınıf var .
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();
}
Kripto kodu yazarken, çalışma zamanının verilere bağlı olmadığından emin olmak için veriye bağımlı dallardan ve tablo aramalarından kaçınmak yaygındır, çünkü veriye bağlı zamanlama yan kanal saldırılarına neden olabilir.
Aynı zamanda oldukça hızlı.
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
Bütün umutları terk et, buraya giren siz
Tuhaf bitlerle ilgili bir açıklama:
bytes[i] >> 4
bir baytın yüksek kırıntısını bytes[i] & 0xF
çıkarır bir baytın düşük kırıntısını çıkarırb - 10
< 0
değerler için b < 10
bir ondalık haneli olacak, >= 0
değerler için b > 10
bir mektup olacak, A
için F
.i >> 31
İşaretli bir 32 bit tam sayı üzerinde kullanmak , işaret uzantısı sayesinde işareti ayıklar. Olacak -1
için i < 0
ve 0
için i >= 0
.(b-10)>>31
olacak 0
harfler ve -1
basamak için.0
ve b
10 ila 15 aralığındadır. Bunu A
(65) ila F
(70) ile eşleştirmek istiyoruz , bu da 55 ( 'A'-10
) eklemeyi ima ediyor .b
0 ila 9 aralığından 0
(48) ila 9
(57) aralığına eşleyecek şekilde uyarlamak istiyoruz . Bu, -7 ( '0' - 55
) olması gerektiği anlamına gelir . & -7
beri kullanabiliriz .(0 & -7) == 0
(-1 & -7) == -7
Dikkat edilmesi gereken bazı noktalar:
c
Ölçümün hesaplanmasının i
daha ucuz olduğunu gösterdiğinden , indekslemek için ikinci bir döngü değişkeni kullanmadım .i < bytes.Length
Döngünün üst sınırı olarak kullanmak JITter'ın sınır kontrollerini ortadan kaldırmasına izin verir bytes[i]
, bu yüzden bu varyantı seçtim.b
Bir int yapmak , byte ve byte arasında gereksiz dönüşümlere izin verir.hex string
hiç byte[] array
?
87 + b + (((b-10)>>31)&-39)
byte[] array
Kelimenin tam anlamıyla bir dizi bayt dizisi anlamına gelen " " dediniz veya byte[][]
. Sadece alay ediyordum.
Daha fazla esneklik istiyorsanız BitConverter
, ancak 1990'ların bu karmaşık açık döngülerini istemiyorsanız, şunları yapabilirsiniz:
String.Join(String.Empty, Array.ConvertAll(bytes, x => x.ToString("X2")));
Veya .NET 4.0 kullanıyorsanız:
String.Concat(Array.ConvertAll(bytes, x => x.ToString("X2")));
(İkincisi, orijinal yayına yapılan bir yorumdan.)
Başka bir arama tablosu tabanlı yaklaşım. Bu, bayt başına arama tablosu yerine her bayt için yalnızca bir arama tablosu kullanır.
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);
}
Ben de kullanarak bu varyantlarını test ushort
, struct{char X1, X2}
, struct{byte X1, X2}
arama tablosundaki.
Derleme hedefine (x86, X64) bağlı olarak, bunlar ya yaklaşık olarak aynı performansa sahipti ya da bu varyanttan biraz daha yavaştı.
Ve daha da yüksek performans için, unsafe
kardeşi:
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);
}
Veya doğrudan dizeye yazmanın kabul edilebilir olduğunu düşünüyorsanız:
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;
}
Span
bunun yerine şimdi kullanılabilir mi acaba unsafe
??
BitConverter.ToString yöntemini kullanabilirsiniz:
byte[] bytes = {0, 1, 2, 4, 8, 16, 32, 64, 128, 256}
Console.WriteLine( BitConverter.ToString(bytes));
Çıktı:
00-01-02-04-08-10-20-40-80-FF
Daha fazla bilgi: BitConverter.ToString Yöntemi (Byte [])
Bugün sadece aynı sorunla karşılaştım ve bu kodla karşılaştım:
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);
}
Kaynak: Forum post byte [] Hex String dizisi dizisi (PZahra'nın gönderisine bakınız). 0x önekini kaldırmak için kodu biraz değiştirdim.
Kod için bazı performans testleri yaptım ve BitConverter.ToString () (patridge yazısına göre en hızlı) kullanmaktan neredeyse sekiz kat daha hızlıydı.
Bu bir cevaptır revizyon 4 arasında Tomalak son derece popüler cevap (ve sonraki düzenlemeleri).
Bu düzenlemenin yanlış olduğunu ve neden geri alınabileceğini açıklayacağım. Yol boyunca, bazı iç öğeler hakkında bir iki şey öğrenebilir ve erken optimizasyonun gerçekte ne olduğunu ve sizi nasıl ısırdığını gösteren başka bir örnek görebilirsiniz.
tl; dr: Sadece kullanın Convert.ToByte
ve String.Substring
aceleniz varsa (aşağıdaki "Orijinal kod"), yeniden uygulamak istemiyorsanız en iyi kombinasyon budur Convert.ToByte
. Performansa ihtiyacınızConvert.ToByte
varsa kullanmayan daha gelişmiş bir şey kullanın (diğer yanıtlara bakın) . Do not dışında başka bir şey kullanmak ile birlikte birisi bu cevabın yorumlarında bu konuda söylenecek bir şey ilginç olmadıkça,.String.Substring
Convert.ToByte
Uyarı: Bu cevap eskimiş hale gelebilir eğer bir Convert.ToByte(char[], Int32)
aşırı yük çerçevesinde uygulanmaktadır. Bunun yakında gerçekleşmesi olası değildir.
Genel bir kural olarak, "zamanından önce optimizasyon yapma" demekten pek hoşlanmıyorum, çünkü "erken" ne zaman kimse bilmiyor. Optimizasyon yapıp yapmayacağınıza karar verirken göz önünde bulundurmanız gereken tek şey: "Optimizasyon yaklaşımlarını doğru bir şekilde araştıracak zamanım ve kaynakım var mı?". Bunu yapmazsanız proje daha olgun kadar, o zaman çok erken, bekleyecek ya da performansa ihtiyacınız kadar (gerçek bir ihtiyaç varsa, o zaman olacaktır yapmak zamanı). Bu arada, bunun yerine çalışabilecek en basit şeyi yapın.
Orijinal kod:
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;
}
Revizyon 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;
}
Düzeltme bundan kaçınır String.Substring
ve StringReader
bunun yerine kullanır . Verilen sebep:
Düzenleme: Tek geçişli ayrıştırıcı kullanarak uzun dizelerin performansını aşağıdaki gibi artırabilirsiniz:
Peki, referans kodunaString.Substring
bakıldığında, zaten "tek geçişli" dir; ve neden olmasın? Vekil çiftlerde değil, bayt düzeyinde çalışır.
Ancak yeni bir dize ayırır, ancak Convert.ToByte
yine de geçmek için bir tane ayırmanız gerekir. Ayrıca, revizyonda sağlanan çözüm her yinelemede başka bir nesne tahsis eder (iki karakter dizisi); bu ayırmayı güvenli bir şekilde döngü dışına koyabilir ve bundan kaçınmak için diziyi yeniden kullanabilirsiniz.
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;
}
Her onaltılı numeral
, iki basamak (sembol) kullanan tek bir sekizliyi temsil eder.
Ama sonra neden StringReader.Read
iki kez aramalısınız ? Sadece ikinci aşırı yüklenmesini çağırın ve iki karakter dizisindeki iki karakteri aynı anda okumasını isteyin; ve arama miktarını iki azaltabilirsiniz.
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;
}
Geriye kalan tek şey, yalnızca "değer" eklenen, _pos
kendinizi ( j
örneğin,) bildirebileceğiniz paralel bir dizin (dahili ), gereksiz uzunluk değişkeni (dahili _length
) ve girdiye gereksiz referans olan bir dize okuyucusudur. dize (dahili _s
). Başka bir deyişle, işe yaramaz.
Read
"Okuduğunu" merak ediyorsanız , koda bakmanız yeterlidir , tek yapmanız String.CopyTo
gereken girdi dizgisini aramaktır. Gerisi ihtiyacımız olmayan değerleri korumak için sadece kitap tutma yüküdür.
Yani, dize okuyucuyu zaten kaldırın ve CopyTo
kendinizi arayın ; daha basit, daha net ve daha verimlidir.
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;
}
Gerçekten j
paralel iki basamaklı bir dizine ihtiyacınız var i
mı? Tabii ki değil, sadece i
iki ile çarpın (derleyici bir ek için optimize edebilmelidir).
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;
}
Çözüm şimdi neye benziyor? O başındaydı Tam gibi sadece kullanmak yerine String.Substring
dize tahsis ve ona veri kopyalamak, size karşı onaltılık rakamlarını kopyalamak hangi bir aracı dizi kullanıyorsanız, o zaman kendinizi dize tahsis ve veri kopyalamak tekrar dan dizi ve dizeye (dizgi yapıcısına ilettiğinizde). Dize zaten stajyer havuzundaysa ikinci kopya optimize edilebilir, ancak daha sonra String.Substring
bu gibi durumlarda önlenebilir.
Aslında, String.Substring
tekrar bakarsanız , dizeyi normalde yapabileceğinizden daha hızlı tahsis etmek için dizelerin nasıl oluşturulduğuna dair bazı düşük düzeyli dahili bilgiler kullandığını görürsünüz ve CopyTo
önlemek için doğrudan orada kullanılan kodun tamamını sıralar çağrı yükü.
String.Substring
Manuel yöntem
Sonuç? Kullanmak istiyorsanızConvert.ToByte(String, Int32)
(çünkü bu işlevselliği kendiniz yeniden uygulamak istemiyorsanız), yenmenin bir yolu yok gibi görünüyor String.Substring
; tek yaptığınız tekerleği yeniden icat ederek daireler çizmek (sadece en uygun olmayan malzemelerle).
Aşırı performansa ihtiyacınız yoksa Convert.ToByte
ve String.Substring
öğelerini kullanmanın tamamen geçerli bir seçim olduğunu unutmayın. Unutmayın: Bir alternatifi yalnızca bunun nasıl düzgün çalıştığını araştıracak zamanınız ve kaynaklarınız varsa seçin.
Eğer Convert.ToByte(char[], Int32)
olsaydı, işler elbette farklı olurdu (yukarıda tarif ettiğim şeyi yapmak ve tamamen kaçınmak mümkün olurdu String
).
Ben String.Substring
de "kaçınarak " daha iyi performans rapor insanlar kaçının şüpheli, Convert.ToByte(String, Int32)
yine de performans gerekiyorsa gerçekten yapmanız gerekir. Bunu yapmak için tüm farklı yaklaşımları keşfetmek için sayısız cevaplara bakın.
Yasal Uyarı: Referans kaynağının güncel olduğunu doğrulamak için çerçevenin en son sürümünü deşifre etmedim, sanırım öyle.
Şimdi, her şey iyi ve mantıklı geliyor, umarım şimdiye kadar ulaşmayı başarabilirseniz bile açıktır. Ama doğru mu?
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
Evet!
Tezgah çerçevesi için Partridge sahne, kesmek kolaydır. Kullanılan girdi, 100.000 bayt uzunluğunda bir dize oluşturmak için 5000 kez tekrarlanan aşağıdaki SHA-1 karmasıdır.
209113288F93A9AB8E474EA78D899AFDBB874355
İyi eğlenceler! (Ancak denetleme ile optimize edin.)
@CodesInChaos tarafından yanıtlamanın tamamlayıcısı (ters yöntem)
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;
}
Açıklama:
& 0x0f
küçük harfleri de desteklemektir
hi = hi + 10 + ((hi >> 31) & 7);
aynıdır:
hi = ch-65 + 10 + (((ch-65) >> 31) & 7);
'0' için .. '9' da aynıdır hi = ch - 65 + 10 + 7;
olan hi = ch - 48
(bu çünkü 0xffffffff & 7
).
'A' .. 'F' için hi = ch - 65 + 10;
(bu yüzden 0x00000000 & 7
).
'A' .. 'f' için büyük sayılar kullanmalıyız, bu yüzden 0
kullanarak bazı bitler yaparak 32'yi varsayılan sürümden çıkarmalıyız & 0x0f
.
65 için kod 'A'
48 koddur '0'
Şekil 7, ASCII tablosu ( ) arasındaki '9'
ve 'A'
içindeki harflerin sayısıdır ...456789:;<=>?@ABCD...
.
Bu sorun, bir arama tablosu kullanılarak da çözülebilir. Bu, hem kodlayıcı hem de kod çözücü için az miktarda statik bellek gerektirir. Ancak bu yöntem hızlı olacaktır:
Benim çözüm kodlama tablosu için 1024 bayt ve kod çözme için 256 bayt kullanır.
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 *
* bu çözüm
Kod çözme sırasında IOException ve IndexOutOfRangeException oluşabilir (bir karakterin değeri çok yüksekse> 256). Akımların veya dizilerin kodunun çözülmesi / kodlanması için yöntemler uygulanmalıdır, bu sadece bir kavram kanıtıdır.
Bu harika bir gönderi. Waleed'in çözümünü seviyorum. Patridge testi ile çalıştırmadım ama oldukça hızlı görünüyor. Ben de bir hex dizisi bir bayt dizisine dönüştürerek, ters işlem gerekli, bu yüzden ben Waleed çözümünün bir ters olarak yazdı. Tomalak'ın orijinal çözümünden daha hızlı olup olmadığından emin değilim. Yine, patridge testi ile ters işlemi de yapmadım.
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;
}
hexString[i] &= ~0x20;
Neden karmaşık? Visual Studio 2008'de bu basittir:
C #:
string hex = BitConverter.ToString(YourByteArray).Replace("-", "");
VB:
Dim hex As String = BitConverter.ToString(YourByteArray).Replace("-", "")
Burada birçok cevaba kazık değil, ama oldukça uygun (~ 4.5x kabul edilenden daha iyi), hex dize ayrıştırıcı basit bir uygulama bulundu. İlk olarak, testlerimden çıktı (ilk parti benim uygulamam):
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 ve 'BitConverter'd' hatları doğruluğu test etmek için orada. Eşit olduklarına dikkat edin.
Hayata geçirme:
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;
}
Bazı şeyleri denedim unsafe
ve (açıkça gereksiz) karakter-nibble if
dizisini başka bir yönteme taşıdım , ancak bu en hızlıydı.
(Sorunun yarısını cevapladığını kabul ediyorum. String-> byte [] dönüşümünün yeterince temsil edilmediğini, byte [] -> string açısının iyi kapandığını hissettim. Bu nedenle, bu cevap.)
Güvenli sürümler:
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;
}
}
}
Güvensiz versiyonlar Performansı tercih eden ve güvensizlikten korkmayanlar için. Yaklaşık% 35 daha hızlı ToHex ve% 10 daha hızlı FromHex.
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 Dönüştürme işlevi çağrılan her yanlış olduğunda alfabeyi başlatan karşılaştırma testi için, alfabe const (string için) veya statik salt okunur (char []) olmalıdır. Daha sonra byte [] öğesinin alfabeye dayalı olarak dönüştürülmesi, bayt işleme sürümleri kadar hızlı olur.
Ve tabii ki test Release (optimizasyon ile) ve "JIT optimizasyonunu bastır" kapalı hata ayıklama seçeneği ile derlenmelidir (kod hata ayıklanabilir olması gerekiyorsa "Sadece Kodumu Etkinleştir" için de geçerlidir).
Waleed Eissa kodu (Hex String - Byte Array) için ters fonksiyon:
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;
}
Küçük harf destekli Waleed Eissa işlevi:
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);
}
Genişletme yöntemleri (sorumluluk reddi: tamamen denenmemiş kod, 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();
}
}
vb. Tomalak'ın üç çözümünden birini kullanın (sonuncusu bir dizgide bir uzantı yöntemi).
Microsoft'un geliştiricilerinden, güzel ve basit bir dönüşüm:
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();
}
Yukarıdakiler temiz ve kompakt olsa da, performans bağımlıları numaralandırıcılar kullanarak çığlık atacak. Tomalak'ın orijinal cevabının geliştirilmiş bir versiyonuyla en yüksek performansı elde edebilirsiniz :
public static string ByteArrayToString(byte[] ba)
{
StringBuilder hex = new StringBuilder(ba.Length * 2);
for(int i=0; i < ba.Length; i++) // <-- Use for loop is faster than foreach
hex.Append(ba[i].ToString("X2")); // <-- ToString is faster than AppendFormat
return hex.ToString();
}
Bu, şimdiye kadar burada gördüğüm tüm rutinlerin en hızlısı. Sadece benim sözüme güvenmeyin ... performans her rutini test edin ve CIL kodunu kendiniz kontrol edin.
b.ToSting("X2")
.
Ve bir SQL dizesine eklemek için (komut parametrelerini kullanmıyorsanız):
public static String ByteArrayToSQLHexString(byte[] Source)
{
return = "0x" + BitConverter.ToString(Source).Replace("-", "");
}
Source == null
yoksa Source.Length == 0
bir sorun var efendim!
Hız açısından, buradaki her şeyden daha iyi gibi görünüyor:
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);
}
Çalışmayı önerdiğin kodu alamadım, Olipro. hex[i] + hex[i+1]
görünüşe göre bir döndü int
.
Ancak, Waleeds kodundan bazı ipuçları alarak ve bunu birlikte çekiçleyerek bazı başarılar elde ettim. Cehennem kadar çirkin ama testlerime göre (patenler test mekanizması kullanılarak) diğerlerine kıyasla 1/3 oranında çalışıyor ve çalışıyor gibi görünüyor. Giriş boyutuna bağlı olarak. İlk önce 0-9'u ayırmak için?: S arasında geçiş yapmak, harflerden daha fazla sayı olduğundan, muhtemelen biraz daha hızlı sonuç verir.
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'ın bu sürümü daha hızlı olabilir.
Raporlarımdan:
...
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);
}
Ve bence bu bir optimizasyon:
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);
}
Onaltılık kodları çözmek için bit-kemancı kullanan bir cevabım olduğu için bu bit kıpır kıpır yarışmaya gireceğim . Arama dizileri de StringBuilder
zaman alacağından , karakter dizilerinin kullanılmasının daha da hızlı olabileceğini unutmayın .
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 kodundan dönüştürüldü.
Char[]
ve Char
ints yerine dahili olarak kullanmalıyım ...
Performans için drphrozens çözeltisi ile giderdim. Kod çözücü için küçük bir optimizasyon, her iki karakter için de "<< 4" den kurtulmak için bir tablo kullanmak olabilir.
Açıkça, iki yöntem çağrısı maliyetlidir. Giriş veya çıkış verilerinde bir tür kontrol yapılırsa (CRC, sağlama toplamı veya herhangi bir if (b == 255)...
şey olabilir) atlanabilir ve böylece yöntem tamamen çağrılır.
Kullanılması offset++
ve offset
yerine offset
ve offset + 1
bazı teorik fayda verebilir ama bu daha iyi benden daha derleyici kolları şüpheli.
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]));
}
Bu sadece başımın üstünde ve test edilmedi veya kıyaslanamadı.
Çeşitlilik için bir başka varyasyon:
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;
}
Hız için optimize edilmemiştir, ancak çoğu yanıttan daha fazla 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
İki kemirme işlemini bire katlayan iki mashup.
Muhtemelen oldukça verimli versiyon:
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 );
}
Çökmüş linq-bit-hack versiyonu:
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))) );
}
Ve tersi:
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;
}
Başka bir yol stackalloc
GC bellek basıncını düşürmek için kullanmaktır :
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);
}
İşte benim çekimim. Dize ve bayt genişletmek için bir çift uzantı sınıfları oluşturdum. Büyük dosya testinde, performans Bayt Manipülasyon 2 ile karşılaştırılabilir.
ToHexString için aşağıdaki kod, arama ve kaydırma algoritmasının optimize edilmiş bir uygulamasıdır. Behrooz'unkiyle neredeyse aynı, ancak foreach
yinelemek için bir kullanarak ve bir sayaç açıkça endekslemeden daha hızlıfor
.
Makinemde Byte Manipulation 2'nin arkasında 2. sırada geliyor ve çok okunabilir bir kod. Aşağıdaki test sonuçları da ilgi çekicidir:
ToHexStringCharArrayWithCharArrayLookup: 41.589.69 ortalama keneler (1000'den fazla koşu), 1.5X ToHexStringCharArrayWithStringLookup: 50.764.06 ortalama keneler (1000'den fazla koşu), 1.2X ToHexStringSharingBuilderWu
Yukarıdaki sonuçlara dayanarak, şu sonuca varmak güvenli görünüyor:
İşte kod:
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;
}
}
}
Kodumu @ patridge'in makinemdeki test projesine koyduğumda aldığım test sonuçları aşağıda. Ayrıca onaltılık bir bayt dizisine dönüştürmek için bir test ekledim. Kodumu kullanan test çalıştırmaları ByteArrayToHexViaOptfiedLookupAndShift ve HexToByteArrayViaByteManipulation'dır. HexToByteArrayViaConvertToByte XXXX'ten alınmıştır. HexToByteArrayViaSoapHexBinary @ Mykroft'un cevabından biridir.
Intel Pentium III Xeon işlemci
Cores: 4 <br/> Current Clock Speed: 1576 <br/> Max Clock Speed: 3092 <br/>
Bayt dizisini onaltılık dize temsiline dönüştürme
ByteArrayToHexViaByteManipulation2: 39,366,64 ortalama keneler (1000'den fazla çalışma), 22,4X
ByteArrayToHexViaOptfiedLookupAndShift: 41.588.64 ortalama keneler (1000'den fazla çalışma), 21.2X
ByteArrayToHexViaLookup: 55.509.56 ortalama keneler (1000'den fazla koşu), 15.9X
ByteArrayToHexViaByteMipipülasyon: 65.349.12 ortalama keneler (1000'den fazla çalışma), 13.5X
ByteArrayToHexViaLookupAndShift: 86.926,87 ortalama keneler (1000'den fazla çalışma), 10.2X
ByteArrayToHexStringViaBitConverter: 139.353,73 ortalama keneler (1000'den fazla çalışma), 6.3X
ByteArrayToHexViaSoapHexBinary: 314.598,77 ortalama keneler (1000'den fazla çalışma), 2.8X
ByteArrayToHexStringViaStringBuilderForEachByteToString: 344,264,63 ortalama keneler (1000'den fazla çalışma), 2.6X
ByteArrayToHexStringViaStringBuilderAggregateByteToString: 382,623,44 ortalama keneler (1000'den fazla çalışma), 2.3X
ByteArrayToHexStringViaStringBuilderForEachAppendFormat: 818,111,95 ortalama keneler (1000'den fazla çalışma), 1.1X
ByteArrayToHexStringViaStringConcatArrayConvertAll: 839,244.84 ortalama keneler (1000'den fazla çalışma), 1.1X
ByteArrayToHexStringViaStringBuilderAggregateAppendFormat: 867.303.98 ortalama keneler (1000'den fazla çalışma), 1.0X
ByteArrayToHexStringViaStringJoinArrayConvertAll: 882,710,28 ortalama keneler (1000'den fazla çalışma), 1.0X
Başka bir hızlı fonksiyon ...
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;
}