Bagaimana Anda bisa mengubah array byte ke string heksadesimal, dan sebaliknya?
Bagaimana Anda bisa mengubah array byte ke string heksadesimal, dan sebaliknya?
Jawaban:
Antara:
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();
}
atau:
public static string ByteArrayToString(byte[] ba)
{
return BitConverter.ToString(ba).Replace("-","");
}
Bahkan ada lebih banyak varian untuk melakukannya, misalnya di sini .
Konversi terbalik akan seperti ini:
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;
}
Menggunakan Substring
adalah pilihan terbaik dalam kombinasi dengan Convert.ToByte
. Lihat jawaban ini untuk informasi lebih lanjut. Jika Anda membutuhkan kinerja yang lebih baik, Anda harus menghindari Convert.ToByte
sebelum Anda dapat jatuh SubString
.
Catatan: pemimpin baru pada 2015-08-20.
Saya menjalankan masing-masing dari berbagai metode konversi melalui beberapa Stopwatch
pengujian kinerja kasar , menjalankan dengan kalimat acak (n = 61, 1000 iterasi) dan menjalankan dengan teks Project Gutenburg (n = 1.238.957, 150 iterasi). Inilah hasilnya, kira-kira dari yang tercepat hingga yang terlambat Semua pengukuran dalam kutu ( 10.000 kutu = 1 ms ) dan semua catatan relatif dibandingkan dengan StringBuilder
implementasi [paling lambat] . Untuk kode yang digunakan, lihat di bawah ini atau repo framework tes di mana saya sekarang memelihara kode untuk menjalankan ini.
PERINGATAN: Jangan mengandalkan statistik ini untuk sesuatu yang konkret; mereka hanyalah contoh sampel data. Jika Anda benar-benar membutuhkan kinerja terbaik, silakan uji metode ini di lingkungan yang mewakili kebutuhan produksi Anda dengan perwakilan data tentang apa yang akan Anda gunakan.
unsafe
(via CodesInChaos) (ditambahkan untuk menguji repo oleh airbreather )
BitConverter
(via Tomalak)
{SoapHexBinary}.ToString
(via Mykroft)
{byte}.ToString("X2")
(menggunakan foreach
) (berasal dari jawaban Will Dean)
{byte}.ToString("X2")
(menggunakan {IEnumerable}.Aggregate
, membutuhkan System.Linq) (melalui Mark)
Array.ConvertAll
(menggunakan string.Join
) (via Will Dean)
Array.ConvertAll
(menggunakan string.Concat
, membutuhkan .NET 4.0) (via Will Dean)
{StringBuilder}.AppendFormat
(menggunakan foreach
) (via Tomalak)
{StringBuilder}.AppendFormat
(menggunakan {IEnumerable}.Aggregate
, membutuhkan System.Linq) (berasal dari jawaban Tomalak)
Tabel pencarian telah mengambil alih manipulasi byte atas. Pada dasarnya, ada beberapa bentuk precomputing apa yang diberikan nibble atau byte dalam hex. Kemudian, saat Anda merobek data, Anda cukup mencari bagian selanjutnya untuk melihat apa string hex itu. Nilai itu kemudian ditambahkan ke output string yang dihasilkan dalam beberapa cara. Untuk manipulasi byte yang lama, yang berpotensi lebih sulit untuk dibaca oleh beberapa pengembang, adalah pendekatan dengan performa terbaik.
Taruhan terbaik Anda masih akan menemukan beberapa data representatif dan mencobanya di lingkungan seperti produksi. Jika Anda memiliki batasan memori yang berbeda, Anda dapat memilih metode dengan alokasi yang lebih sedikit daripada yang lebih cepat tetapi mengonsumsi lebih banyak memori.
Jangan ragu untuk bermain dengan kode pengujian yang saya gunakan. Versi disertakan di sini tetapi jangan ragu untuk mengkloning repo dan menambahkan metode Anda sendiri. Kirimkan permintaan tarik jika Anda menemukan sesuatu yang menarik atau ingin membantu meningkatkan kerangka pengujian yang digunakannya.
Func<byte[], string>
) ke /Tests/ConvertByteArrayToHexString/Test.cs.TestCandidates
kembali di kelas yang sama.GenerateTestInput
kelas yang sama.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();
}
Menambahkan jawaban Waleed untuk analisis. Cukup cepat.
string.Concat
Array.ConvertAll
Varian ditambahkan untuk kelengkapan (memerlukan .NET 4.0). Setara denganstring.Join
versi.
Repo uji mencakup lebih banyak varian seperti StringBuilder.Append(b.ToString("X2"))
. Tidak ada yang mengecewakan hasilnya. foreach
lebih cepat daripada {IEnumerable}.Aggregate
, misalnya, tetapi BitConverter
masih menang.
Menambahkan SoapHexBinary
jawaban Mykroft untuk analisis, yang mengambil alih tempat ketiga.
Menambahkan jawaban manipulasi byte CodesInChaos, yang mengambil alih tempat pertama (dengan margin besar pada blok teks besar).
Ditambahkan jawaban pencarian Nathan Moinvaziri dan varian dari blog Brian Lambert. Keduanya agak cepat, tetapi tidak memimpin pada mesin uji yang saya gunakan (AMD Phenom 9750).
Menambahkan jawaban lookup berbasis byte baru @ CodesInChaos. Tampaknya memimpin pada tes kalimat dan tes teks lengkap.
Menambahkan pengoptimalan dan varian airbreatherunsafe
ke repo jawaban ini . Jika Anda ingin bermain di permainan yang tidak aman, Anda bisa mendapatkan beberapa keuntungan kinerja besar dibandingkan pemenang teratas sebelumnya baik dalam string pendek maupun teks besar.
bytes.ToHexStringAtLudicrousSpeed()
).
Ada kelas yang disebut SoapHexBinary yang melakukan apa yang Anda inginkan.
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();
}
Saat menulis kode kripto, adalah hal biasa untuk menghindari pencarian cabang dan tabel yang bergantung pada data untuk memastikan runtime tidak bergantung pada data, karena pengaturan waktu yang bergantung pada data dapat menyebabkan serangan saluran samping.
Ini juga cukup cepat.
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
Buang semua harapan, kamu yang masuk ke sini
Penjelasan dari si aneh yang mengutak-atik:
bytes[i] >> 4
mengekstrak nibble byte yang tinggibytes[i] & 0xF
yang rendahb - 10
< 0
untuk nilai b < 10
, yang akan menjadi angka desimal >= 0
untuk nilai b > 10
, yang akan menjadi surat dariA
ke F
.i >> 31
pada integer 32 bit yang ditandatangani mengekstrak tanda, terima kasih untuk menandatangani ekstensi. Itu akan -1
untuk i < 0
dan 0
untuk i >= 0
.(b-10)>>31
akan 0
untuk huruf dan -1
untuk angka.0
, dan b
berada dalam kisaran 10 hingga 15. Kami ingin memetakannya ke A
(65) hingga F
(70), yang berarti menambahkan 55 ( 'A'-10
).b
dari rentang 0 hingga 9 ke kisaran 0
(48) hingga 9
(57). Ini berarti harus menjadi -7 ( '0' - 55
). & -7
sejak (0 & -7) == 0
dan (-1 & -7) == -7
.Beberapa pertimbangan lebih lanjut:
c
, karena pengukuran menunjukkan bahwa menghitung itu i
lebih murah.i < bytes.Length
seperti batas atas dari loop memungkinkan JITter untuk menghilangkan batas cek bytes[i]
, jadi saya memilih varian itu.b
int memungkinkan konversi yang tidak perlu dari dan ke byte.hex string
untuk byte[] array
?
87 + b + (((b-10)>>31)&-39)
byte[] array
", yang secara harfiah berarti array array byte, atau byte[][]
. Saya hanya mengolok-olok.
Jika Anda menginginkan lebih banyak fleksibilitas daripada BitConverter
, tetapi tidak ingin loop eksplisit gaya 1990-an yang kikuk, maka Anda dapat melakukannya:
String.Join(String.Empty, Array.ConvertAll(bytes, x => x.ToString("X2")));
Atau, jika Anda menggunakan .NET 4.0:
String.Concat(Array.ConvertAll(bytes, x => x.ToString("X2")));
(Yang terakhir dari komentar pada posting asli.)
Pendekatan lain berdasarkan tabel pencarian. Yang ini hanya menggunakan satu tabel pencarian untuk setiap byte, bukan tabel pencarian per gigitan.
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);
}
Saya juga diuji varian ini menggunakan ushort
, struct{char X1, X2}
,struct{byte X1, X2}
di tabel.
Bergantung pada target kompilasi (x86, X64) yang memiliki kinerja yang kurang lebih sama atau sedikit lebih lambat dari varian ini.
Dan untuk kinerja yang lebih tinggi, unsafe
saudara kandungnya:
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);
}
Atau jika Anda menganggap dapat langsung menulis ke string:
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
dapat digunakan sekarang bukan unsafe
??
Anda dapat menggunakan metode BitConverter.ToString:
byte[] bytes = {0, 1, 2, 4, 8, 16, 32, 64, 128, 256}
Console.WriteLine( BitConverter.ToString(bytes));
Keluaran:
00-01-02-04-08-10-20-40-80-FF
Informasi lebih lanjut: Metode BitConverter.ToString (Byte [])
Saya baru saja mengalami masalah yang sama hari ini, dan saya menemukan kode ini:
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);
}
Sumber: Forum post byte [] Array ke Hex String (lihat posting oleh PZahra). Saya memodifikasi kode sedikit untuk menghapus awalan 0x.
Saya melakukan beberapa pengujian kinerja untuk kode dan hampir delapan kali lebih cepat daripada menggunakan BitConverter.ToString () (tercepat menurut posting patridge).
Ini adalah jawaban untuk revisi 4 dari jawaban Tomalak yang sangat populer (dan suntingan berikutnya).
Saya akan membuat kasus bahwa pengeditan ini salah, dan menjelaskan mengapa itu dapat dikembalikan. Sepanjang jalan, Anda mungkin belajar satu atau dua hal tentang beberapa internal, dan melihat contoh lain dari apa sebenarnya optimasi prematur dan bagaimana hal itu dapat menggigit Anda.
tl; dr: Cukup gunakan Convert.ToByte
dan String.Substring
jika Anda sedang terburu-buru ("Kode asli" di bawah), itu kombinasi terbaik jika Anda tidak ingin menerapkan kembali Convert.ToByte
. Gunakan sesuatu yang lebih maju (lihat jawaban lain) yang tidak digunakan Convert.ToByte
jika Anda membutuhkan kinerja. Jangan tidak menggunakan apa-apa lagi selain String.Substring
dalam kombinasi denganConvert.ToByte
, kecuali seseorang memiliki sesuatu yang menarik untuk mengatakan tentang ini di komentar dari jawaban ini.
peringatan: Jawaban ini mungkin menjadi usang jika suatu Convert.ToByte(char[], Int32)
kelebihan diimplementasikan dalam rangka. Ini tidak mungkin terjadi segera.
Sebagai aturan umum, saya tidak suka mengatakan "jangan optimalkan secara prematur", karena tidak ada yang tahu kapan "prematur" itu. Satu-satunya hal yang harus Anda pertimbangkan ketika memutuskan apakah akan mengoptimalkan atau tidak adalah: "Apakah saya punya waktu dan sumber daya untuk menyelidiki pendekatan optimasi dengan benar?". Jika tidak, maka terlalu cepat, tunggu sampai proyek Anda lebih matang atau sampai Anda membutuhkan kinerja (jika ada kebutuhan nyata, maka Anda akan meluangkan waktu). Sementara itu, lakukan hal paling sederhana yang mungkin bisa berhasil.
Kode asli:
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;
}
Revisi 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;
}
Revisi menghindari String.Substring
dan menggunakan StringReader
gantinya. Alasan yang diberikan adalah:
Sunting: Anda dapat meningkatkan kinerja untuk string panjang dengan menggunakan pengurai pass tunggal, seperti:
Nah, melihat kode referensiString.Substring
, itu sudah jelas "single-pass"; dan mengapa tidak? Ini beroperasi pada level byte, bukan pada pasangan pengganti.
Namun memang mengalokasikan string baru, tetapi kemudian Anda harus mengalokasikan satu untuk lewat Convert.ToByte
. Selanjutnya, solusi yang disediakan dalam revisi mengalokasikan objek lain pada setiap iterasi (array dua-char); Anda dapat dengan aman meletakkan alokasi itu di luar loop dan menggunakan kembali array untuk menghindarinya.
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;
}
Setiap heksadesimal numeral
mewakili oktet tunggal menggunakan dua digit (simbol).
Tapi, mengapa menelepon StringReader.Read
dua kali? Panggil saja kelebihan kedua dan minta untuk membaca dua karakter dalam array dua karakter sekaligus; dan kurangi jumlah panggilan dua.
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;
}
Yang tersisa dengan Anda adalah pembaca string yang hanya menambahkan "nilai" adalah indeks paralel (internal _pos
) yang bisa Anda nyatakan sendiri (seperti j
misalnya), variabel panjang redundan (internal _length
), dan referensi redundan untuk input string (internal _s
). Dengan kata lain, itu tidak berguna.
Jika Anda bertanya-tanya bagaimana Read
"membaca", lihat saja kode , yang dilakukannya hanyalah memanggil String.CopyTo
string input. Sisanya hanya biaya pembukuan untuk mempertahankan nilai-nilai yang tidak kita butuhkan.
Jadi, lepaskan pembaca string, dan panggil CopyTo
diri Anda sendiri; lebih sederhana, lebih jelas, dan lebih efisien.
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;
}
Apakah Anda benar-benar membutuhkan j
indeks yang bertambah dalam langkah dua paralel i
? Tentu saja tidak, hanya kalikan i
dengan dua (yang harus dioptimalkan oleh kompiler untuk penambahan).
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;
}
Seperti apa solusinya sekarang? Persis seperti di awal, hanya alih-alih menggunakan String.Substring
untuk mengalokasikan string dan menyalin data ke dalamnya, Anda menggunakan array perantara tempat Anda menyalin angka heksadesimal, kemudian mengalokasikan string sendiri dan menyalin data lagi dari array dan ke dalam string (ketika Anda meneruskannya di konstruktor string). Salinan kedua mungkin dioptimalkan-keluar jika string sudah ada di kolam magang, tetapi kemudian String.Substring
juga akan dapat menghindarinya dalam kasus ini.
Bahkan, jika Anda melihat String.Substring
lagi, Anda melihat bahwa ia menggunakan beberapa pengetahuan internal tingkat rendah tentang bagaimana string dibangun untuk mengalokasikan string lebih cepat dari yang biasanya Anda lakukan, dan itu menguraikan kode yang sama yang digunakan CopyTo
secara langsung di sana untuk menghindari panggilan overhead.
String.Substring
Metode manual
Kesimpulan? Jika Anda ingin menggunakanConvert.ToByte(String, Int32)
(karena Anda tidak ingin menerapkan kembali fungsi itu sendiri), sepertinya tidak ada cara untuk mengalahkan String.Substring
; yang Anda lakukan hanyalah berputar-putar, menciptakan kembali roda (hanya dengan bahan yang kurang optimal).
Perhatikan bahwa menggunakan Convert.ToByte
dan String.Substring
merupakan pilihan yang sangat valid jika Anda tidak membutuhkan kinerja ekstrem. Ingat: hanya memilih alternatif jika Anda memiliki waktu dan sumber daya untuk menyelidiki cara kerjanya dengan benar.
Jika ada Convert.ToByte(char[], Int32)
, tentu saja akan berbeda (mungkin untuk melakukan apa yang saya jelaskan di atas dan sepenuhnya menghindari String
).
Saya menduga bahwa orang yang melaporkan kinerja yang lebih baik dengan "menghindari String.Substring
" juga menghindari Convert.ToByte(String, Int32)
, yang seharusnya Anda lakukan jika Anda memang membutuhkan kinerja. Lihatlah jawaban lain yang tak terhitung jumlahnya untuk menemukan semua pendekatan berbeda untuk melakukan itu.
Penafian: Saya belum mendekompilasi versi terbaru kerangka kerja untuk memverifikasi bahwa sumber referensi terbaru, saya anggap benar.
Sekarang, semuanya terdengar bagus dan logis, semoga bahkan jelas jika Anda sudah berhasil sejauh ini. Tetapi apakah itu benar?
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
Iya!
Props to Partridge untuk kerangka kerja bangku, mudah diretas. Input yang digunakan adalah hash SHA-1 berikut yang diulangi 5000 kali untuk menghasilkan string sepanjang 100.000 byte.
209113288F93A9AB8E474EA78D899AFDBB874355
Selamat bersenang-senang! (Tapi optimalkan dengan moderasi.)
Lengkapi untuk menjawab dengan @CodesInChaos (metode terbalik)
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;
}
Penjelasan:
& 0x0f
adalah untuk mendukung juga huruf kecil
hi = hi + 10 + ((hi >> 31) & 7);
sama dengan:
hi = ch-65 + 10 + (((ch-65) >> 31) & 7);
Untuk '0' .. '9' itu sama dengan hi = ch - 65 + 10 + 7;
yang hi = ch - 48
(ini karena 0xffffffff & 7
).
Untuk 'A' .. 'F' itu hi = ch - 65 + 10;
(ini karena 0x00000000 & 7
).
Untuk 'a' .. 'f' kita harus berjumlah besar sehingga kita harus mengurangi 32 dari versi default dengan membuat beberapa bit 0
dengan menggunakan & 0x0f
.
65 adalah kode untuk 'A'
48 adalah kode untuk '0'
7 adalah jumlah huruf antara '9'
dan 'A'
dalam tabel ASCII ( ...456789:;<=>?@ABCD...
).
Masalah ini juga bisa diselesaikan dengan menggunakan tabel pencarian. Ini akan membutuhkan sejumlah kecil memori statis untuk encoder dan decoder. Namun metode ini akan cepat:
Solusi saya menggunakan 1024 byte untuk tabel penyandian, dan 256 byte untuk decoding.
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 *
* solusi ini
Selama mendekode IOException dan IndexOutOfRangeException dapat terjadi (jika karakter memiliki nilai terlalu tinggi> 256). Metode untuk de / encoding stream atau array harus diimplementasikan, ini hanya bukti konsep.
Ini adalah pos yang bagus. Saya suka solusi Waleed. Saya belum menjalankannya melalui tes patridge tetapi tampaknya cukup cepat. Saya juga membutuhkan proses sebaliknya, mengubah string hex menjadi array byte, jadi saya menulisnya sebagai pembalikan dari solusi Waleed. Tidak yakin apakah itu lebih cepat daripada solusi asli Tomalak. Sekali lagi, saya juga tidak menjalankan proses sebaliknya melalui tes 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;
}
hexString[i] &= ~0x20;
Mengapa membuatnya rumit? Ini sederhana di Visual Studio 2008:
C #:
string hex = BitConverter.ToString(YourByteArray).Replace("-", "");
VB:
Dim hex As String = BitConverter.ToString(YourByteArray).Replace("-", "")
Bukan untuk menumpuk banyak jawaban di sini, tapi saya menemukan yang cukup optimal (~ 4.5x lebih baik daripada diterima), implementasi langsung dari pengurai string hex. Pertama, output dari tes saya (batch pertama adalah implementasi saya):
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
Baris base64 dan 'BitConverter'd' ada untuk menguji kebenarannya. Perhatikan bahwa keduanya sama.
Pelaksanaan:
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;
}
Saya mencoba beberapa hal unsafe
dan memindahkan urutan karakter-ke-gigitan if
ke metode lain, tapi ini adalah yang tercepat yang didapatnya.
(Saya mengakui bahwa ini menjawab setengah dari pertanyaan. Saya merasa bahwa konversi string-> byte [] kurang terwakili, sedangkan sudut string byte [] -> tampaknya tercakup dengan baik. Jadi, jawaban ini.)
Versi aman:
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;
}
}
}
Versi tidak aman Bagi mereka yang lebih suka kinerja dan tidak takut ketidakamanan. Tentang 35% lebih cepat ToHex dan 10% lebih cepat DariHex.
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 Untuk pengujian benchmark menginisialisasi alfabet setiap kali fungsi konversi yang dipanggil salah, alfabet harus berupa konst (untuk string) atau hanya baca statis (untuk karakter []). Kemudian konversi byte [] berbasis string ke string menjadi secepat versi manipulasi byte.
Dan tentu saja tes harus dikompilasi dalam Rilis (dengan optimasi) dan dengan opsi debug "Suppress JIT optimization" dimatikan (sama untuk "Aktifkan Just My Code" jika kode harus debuggable).
Fungsi terbalik untuk kode 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 berfungsi dengan dukungan huruf kecil:
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);
}
Metode ekstensi (penafian: kode yang sama sekali belum diuji, 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();
}
}
dll. Gunakan salah satu dari tiga solusi Tomalak (dengan yang terakhir menjadi metode ekstensi pada string).
Dari pengembang Microsoft, konversi yang bagus dan sederhana:
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();
}
Sementara di atas bersih dan kompak, pecandu kinerja akan berteriak tentang hal itu menggunakan enumerator. Anda bisa mendapatkan kinerja puncak dengan versi perbaikan dari jawaban asli Tomalak :
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();
}
Ini adalah yang tercepat dari semua rutinitas yang saya lihat diposting di sini sejauh ini. Jangan hanya mengambil kata-kata saya untuk itu ... tes kinerja setiap rutin dan periksa kode CIL untuk Anda sendiri.
b.ToSting("X2")
.
Dan untuk memasukkan ke dalam string SQL (jika Anda tidak menggunakan parameter perintah):
public static String ByteArrayToSQLHexString(byte[] Source)
{
return = "0x" + BitConverter.ToString(Source).Replace("-", "");
}
Source == null
atau Source.Length == 0
kita punya masalah pak!
Dalam hal kecepatan, ini tampaknya lebih baik daripada apa pun di sini:
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);
}
Saya tidak mendapatkan kode yang Anda sarankan untuk bekerja, Olipro. hex[i] + hex[i+1]
ternyata mengembalikan sebuah int
.
Namun, saya berhasil dengan mengambil beberapa petunjuk dari kode Waleeds dan memalu ini bersama-sama. Itu jelek sekali tetapi tampaknya bekerja dan bekerja pada 1/3 waktu dibandingkan dengan yang lain menurut tes saya (menggunakan mekanisme pengujian patridges). Tergantung pada ukuran input. Beralih di sekitar?: S untuk memisahkan 0-9 terlebih dahulu mungkin akan menghasilkan hasil yang sedikit lebih cepat karena ada lebih banyak angka daripada huruf.
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;
}
Versi ByteArrayToHexViaByteManipulation ini bisa lebih cepat.
Dari laporan saya:
...
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);
}
Dan saya pikir ini adalah optimasi:
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);
}
Saya akan memasuki kompetisi bit fiddling ini karena saya punya jawaban yang juga menggunakan bit-fiddling untuk memecahkan kode hexadecimal. Perhatikan bahwa menggunakan array karakter mungkin lebih cepat karena StringBuilder
metode panggilan juga akan memakan waktu.
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;
}
Dikonversi dari kode Java.
Char[]
dan menggunakan Char
internal daripada ints ...
Untuk kinerja saya akan pergi dengan solusi drphrozens. Pengoptimalan kecil untuk dekoder dapat menggunakan tabel untuk salah satu karakter untuk menghilangkan "<< 4".
Jelas kedua panggilan metode ini mahal. Jika semacam pemeriksaan dilakukan baik pada input atau data keluaran (bisa CRC, checksum atau apa pun) yang if (b == 255)...
dapat dilewati dan dengan demikian juga metode panggilan sama sekali.
Menggunakan offset++
dan offset
bukannya offset
dan offset + 1
mungkin memberikan beberapa manfaat teoretis tetapi saya menduga kompiler menangani ini lebih baik daripada saya.
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]));
}
Ini hanya dari atas kepala saya dan belum diuji atau dibandingkan.
Variasi lain untuk keragaman:
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;
}
Tidak dioptimalkan untuk kecepatan, tetapi lebih banyak LINQy daripada sebagian besar jawaban (.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
Dua mashup yang melipat dua operasi menggigit menjadi satu.
Mungkin versi yang sangat efisien:
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 );
}
Versi linq-dengan-bit-peretasan dekaden:
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))) );
}
Dan membalikkan:
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;
}
Cara lain adalah dengan menggunakan stackalloc
untuk mengurangi tekanan memori 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);
}
Ini kesempatan saya. Saya telah membuat sepasang kelas ekstensi untuk memperluas string dan byte. Pada tes file besar, kinerjanya sebanding dengan Byte Manipulation 2.
Kode di bawah ini untuk ToHexString adalah implementasi yang dioptimalkan dari algoritma pencarian dan pergeseran. Itu hampir identik dengan yang oleh Behrooz, tetapi ternyata menggunakan foreach
untuk mengulangi dan penghitung lebih cepat daripada pengindeksan eksplisit for
.
Muncul di tempat ke-2 di belakang Byte Manipulation 2 di komputer saya dan kode yang sangat mudah dibaca. Hasil tes berikut juga menarik:
ToHexStringCharArrayWithCharArrayLookup: 41.589.69 kutu rata-rata (lebih dari 1000 berjalan), 1.5X ToHexStringCharArrayWithStringLookup: 50.764.06 kutu rata-rata (lebih dari 1000 berjalan), 1.2X ToHexStringStringBuilderLebih rata-rata160 berjalan
Berdasarkan hasil di atas, tampaknya aman untuk menyimpulkan bahwa:
Berikut kodenya:
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;
}
}
}
Di bawah ini adalah hasil pengujian yang saya dapatkan ketika saya meletakkan kode saya di proyek pengujian @ patridge di mesin saya. Saya juga menambahkan tes untuk mengkonversi ke array byte dari heksadesimal. Tes berjalan yang menggunakan kode saya adalah ByteArrayToHexViaOptimizedLookupAndShift dan HexToByteArrayViaByteManipulation. HexToByteArrayViaConvertToByte diambil dari XXXX. HexToByteArrayViaSoapHexBinary adalah jawaban dari @ Mykroft.
Prosesor Intel Pentium III Xeon
Cores: 4 <br/> Current Clock Speed: 1576 <br/> Max Clock Speed: 3092 <br/>
Mengubah array byte menjadi representasi string heksadesimal
ByteArrayToHexViaByteManipulation2: 39,366.64 ticks rata-rata (lebih dari 1000 berjalan), 22.4X
ByteArrayToHexViaOptimizedLookupAndShift: 41,588.64 ticks rata-rata (lebih dari 1000 berjalan), 21.2X
ByteArrayToHexViaLookup: rata-rata 55,509,56 kutu (lebih dari 1000 kali berjalan), 15,9X
ByteArrayToHexViaByteManipulasi: Kutu rata-rata 65.349,12 (lebih dari 1000 kali berjalan), 13,5X
ByteArrayToHexViaLookupAndShift: kutu rata-rata 86.926,87 (lebih dari 1000 berjalan), 10.2X
ByteArrayToHexStringViaBitConverter: 139.353,73 kutu rata-rata (lebih dari 1000 berjalan), 6.3X
ByteArrayToHexViaSoapHexBinary: 314.598,77 ticks rata-rata (lebih dari 1000 berjalan), 2.8X
ByteArrayToHexStringViaStringBuilderForEachByteToString: 344.264,63 kutu rata-rata (lebih dari 1000 berjalan), 2.6X
ByteArrayToHexStringViaStringBuilderAggregateByteToString: 382.623,44 kutu rata-rata (lebih dari 1000 berjalan), 2.3X
ByteArrayToHexStringViaStringBuilderForEachAppendFormat: 818.111,95 ticks rata-rata (lebih dari 1000 berjalan), 1.1X
ByteArrayToHexStringViaStringConcatArrayConvertAll: 839.244,84 kutu rata-rata (lebih dari 1000 berjalan), 1.1X
ByteArrayToHexStringViaStringBuilderAggregateAppendFormat: 867.303,98 kutu rata-rata (lebih dari 1000 berjalan), 1.0X
ByteArrayToHexStringViaStringJoinArrayConvertAll: 882.710,28 kutu rata-rata (lebih dari 1000 berjalan), 1.0X
Fungsi cepat lainnya ...
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;
}