C# inder); 整数数字索引=数学Abs((整数)余数); 结果。添加(kBase36Digits[数字索引]); } //按大端排序对字符进行定位 result.Reverse(); //ToArray还将修剪长度预测中使用的多余字符 返回新字符串(result.ToArray()); }
“测试1234.稍微大一点!”将Base64编码为“165kkoorqxin775ct82ist5ysteekll7kaqlcnnu6mfe7ag7e63b5” 在我的机器上解码Base36字符串1000000次需要12.6558909秒(我使用的构建和机器条件与codereview回答中提供的相同)C# inder); 整数数字索引=数学Abs((整数)余数); 结果。添加(kBase36Digits[数字索引]); } //按大端排序对字符进行定位 result.Reverse(); //ToArray还将修剪长度预测中使用的多余字符 返回新字符串(result.ToArray()); },c#,.net,string,hash,md5,C#,.net,String,Hash,Md5,“测试1234.稍微大一点!”将Base64编码为“165kkoorqxin775ct82ist5ysteekll7kaqlcnnu6mfe7ag7e63b5” 在我的机器上解码Base36字符串1000000次需要12.6558909秒(我使用的构建和机器条件与codereview回答中提供的相同) 您提到要处理MD5哈希的字节[],而不是它的十六进制字符串表示形式,因此,我认为此解决方案为您提供的开销最小。您不想将其重新编码到包含此字节数组ascii的字符数组中,还是重新编码到仅使用这些字符
您提到要处理MD5哈希的字节[],而不是它的十六进制字符串表示形式,因此,我认为此解决方案为您提供的开销最小。您不想将其重新编码到包含此字节数组ascii的字符数组中,还是重新编码到仅使用这些字符的新字符串中?字节数组似乎更有意义;-)。你的意思是将每个十六进制数转换为ascii字符?删除该示例,使问题看起来不同。你想要的是基36。。。这样做很复杂:-),但可以做到。这如何保证字符串中的所有字符都在[a-z][0-9]范围内?关于答案:所有这些编码都对所需的字符范围有效。不需要播放。不能保证所有这些都在这个范围内,因为每个字节不仅可以表示字符和数字。如果需要匹配这些要求,可以使用任何字符将每个字节保存为带分隔符的整数。它看起来像:
250a244a…
。这只是一个选项,您可以选择如何匹配[a-z][0-9]。+1最佳选择。但是base64使用了额外的字符(我认为+
和=
)base64也有[A-Z],所以我不能使用它。你确定这会产生好的结果吗?它给出的结果与我的解决方案不同,我的解决方案是。我使用了相同的“chars”字符串。应该从混响字节数组var bi=new biginger(bytes.Concat(new byte[]{0}).Reverse().ToArray()创建大整数代码>@hazzik完成。我还发现了另一个带有“\0”前缀的bug,哇,最后是一个使用BigInteger的编码。由于符号字节的原因,我尝试的所有其他方法都产生了不可靠的结果。谢谢为什么要反转阵列两次?似乎有点多余。不过,您会丢失数据。无法将其转换回原始字符串。如果将数字字符串更改为var digits=“0123456789abcdefghijklmnopqrstuvwxyzabefghijklmnopqrstuvwxyz”,为什么会得到相同的答案
?@Kees:数字
正是可供使用的数字。要实际使用caps,您需要传递适当较高的基数作为第二个和/或第三个参数。。。只是将数字设为静态,并将其长度指定给函数。完美的解决方案,可读性很强。@Jon为什么会有if(newlen==nibbles.Count){nibbles.Add(0);}
?我不明白为什么需要这样做。@Paya:如果newlen==nibbles.Count
并且您没有添加任何内容,那么随后将立即抛出访问nibbles[newlen++]
。FromBase36String
的性能可以通过使用字典进行O(1)查找而不是.IndexOf(chars[x])
来提高,或者更好的方法是:将每个base36字符代码映射回base36数字
数组索引的数组。我将您的代码复制到一个测试项目中,它将{203、77、29、30、215、37、184、136}
编码为“1taukyt738g1h”
,然后将其解码为{53、178、226、225、40、218、71、119}
.1)如果没有实际的性能统计数据来支持这一说法,我很犹豫是否接受使用字典是一种改进。这将需要额外的分配,因为我这里的内容不需要额外的堆分配,并且通常应该具有更好的缓存局部性。阵列映射只需要更多的引导。2) 谢谢,我看到了和你一样的坏行为。当我在一台更好的开发机器上时,我必须对它进行调试,并测试我的Base-N编码器是否也遇到同样的问题
System.Text.Encoding enc = System.Text.Encoding.ASCII;
string myString = enc.GetString(myByteArray);
System.Text.ASCIIEncoding,
System.Text.UnicodeEncoding,
System.Text.UTF7Encoding,
System.Text.UTF8Encoding
Byte[] bytes = new Byte[] { 200, 180, 34 };
string result = String.Join("a", bytes.Select(x => x.ToString()).ToArray());
const string chars = "0123456789abcdefghijklmnopqrstuvwxyz";
// The result is padded with chars[0] to make the string length
// (int)Math.Ceiling(bytes.Length * 8 / Math.Log(chars.Length, 2))
// (so that for any value [0...0]-[255...255] of bytes the resulting
// string will have same length)
public static string ToBaseN(byte[] bytes, string chars, bool littleEndian = true, int len = -1)
{
if (bytes.Length == 0 || len == 0)
{
return String.Empty;
}
// BigInteger saves in the last byte the sign. > 7F negative,
// <= 7F positive.
// If we have a "negative" number, we will prepend a 0 byte.
byte[] bytes2;
if (littleEndian)
{
if (bytes[bytes.Length - 1] <= 0x7F)
{
bytes2 = bytes;
}
else
{
// Note that Array.Resize doesn't modify the original array,
// but creates a copy and sets the passed reference to the
// new array
bytes2 = bytes;
Array.Resize(ref bytes2, bytes.Length + 1);
}
}
else
{
bytes2 = new byte[bytes[0] > 0x7F ? bytes.Length + 1 : bytes.Length];
// We copy and reverse the array
for (int i = bytes.Length - 1, j = 0; i >= 0; i--, j++)
{
bytes2[j] = bytes[i];
}
}
BigInteger bi = new BigInteger(bytes2);
// A little optimization. We will do many divisions based on
// chars.Length .
BigInteger length = chars.Length;
// We pre-calc the length of the string. We know the bits of
// "information" of a byte are 8. Using Log2 we calc the bits of
// information of our new base.
if (len == -1)
{
len = (int)Math.Ceiling(bytes.Length * 8 / Math.Log(chars.Length, 2));
}
// We will build our string on a char[]
var chs = new char[len];
int chsIndex = 0;
while (bi > 0)
{
BigInteger remainder;
bi = BigInteger.DivRem(bi, length, out remainder);
chs[littleEndian ? chsIndex : len - chsIndex - 1] = chars[(int)remainder];
chsIndex++;
if (chsIndex < 0)
{
if (bi > 0)
{
throw new OverflowException();
}
}
}
// We append the zeros that we skipped at the beginning
if (littleEndian)
{
while (chsIndex < len)
{
chs[chsIndex] = chars[0];
chsIndex++;
}
}
else
{
while (chsIndex < len)
{
chs[len - chsIndex - 1] = chars[0];
chsIndex++;
}
}
return new string(chs);
}
public static byte[] FromBaseN(string str, string chars, bool littleEndian = true, int len = -1)
{
if (str.Length == 0 || len == 0)
{
return new byte[0];
}
// This should be the maximum length of the byte[] array. It's
// the opposite of the one used in ToBaseN.
// Note that it can be passed as a parameter
if (len == -1)
{
len = (int)Math.Ceiling(str.Length * Math.Log(chars.Length, 2) / 8);
}
BigInteger bi = BigInteger.Zero;
BigInteger length2 = chars.Length;
BigInteger mult = BigInteger.One;
for (int j = 0; j < str.Length; j++)
{
int ix = chars.IndexOf(littleEndian ? str[j] : str[str.Length - j - 1]);
// We didn't find the character
if (ix == -1)
{
throw new ArgumentOutOfRangeException();
}
bi += ix * mult;
mult *= length2;
}
var bytes = bi.ToByteArray();
int len2 = bytes.Length;
// BigInteger adds a 0 byte for positive numbers that have the
// last byte > 0x7F
if (len2 >= 2 && bytes[len2 - 1] == 0)
{
len2--;
}
int len3 = Math.Min(len, len2);
byte[] bytes2;
if (littleEndian)
{
if (len == bytes.Length)
{
bytes2 = bytes;
}
else
{
bytes2 = new byte[len];
Array.Copy(bytes, bytes2, len3);
}
}
else
{
bytes2 = new byte[len];
for (int i = 0; i < len3; i++)
{
bytes2[len - i - 1] = bytes[i];
}
}
for (int i = len3; i < len2; i++)
{
if (bytes[i] != 0)
{
throw new OverflowException();
}
}
return bytes2;
}
static string BaseConvert(string number, int fromBase, int toBase)
{
var digits = "0123456789abcdefghijklmnopqrstuvwxyz";
var length = number.Length;
var result = string.Empty;
var nibbles = number.Select(c => digits.IndexOf(c)).ToList();
int newlen;
do {
var value = 0;
newlen = 0;
for (var i = 0; i < length; ++i) {
value = value * fromBase + nibbles[i];
if (value >= toBase) {
if (newlen == nibbles.Count) {
nibbles.Add(0);
}
nibbles[newlen++] = value / toBase;
value %= toBase;
}
else if (newlen > 0) {
if (newlen == nibbles.Count) {
nibbles.Add(0);
}
nibbles[newlen++] = 0;
}
}
length = newlen;
result = digits[value] + result; //
}
while (newlen != 0);
return result;
}
var result = BaseConvert(hexEncoded, 16, 36);
public string byteToString(byte[] byteArr)
{
int i;
char[] charArr = new char[byteArr.Length];
for (i = 0; i < byteArr.Length; i++)
{
int byt = byteArr[i] % 36; // 36=num of availible charachters
if (byt < 10)
{
charArr[i] = (char)(byt + 48); //if % result is a digit
}
else
{
charArr[i] = (char)(byt + 87); //if % result is a letter
}
}
return new String(charArr);
}
public string byteToString(byte[] byteArr)
{
int i;
char[] charArr = new char[byteArr.Length*2];
for (i = 0; i < byteArr.Length; i++)
{
charArr[2 * i] = (char)((int)byteArr[i] / 36+48);
int byt = byteArr[i] % 36; // 36=num of availible charachters
if (byt < 10)
{
charArr[2*i+1] = (char)(byt + 48); //if % result is a digit
}
else
{
charArr[2*i+1] = (char)(byt + 87); //if % result is a letter
}
}
return new String(charArr);
}
const int kByteBitCount= 8; // number of bits in a byte
// constants that we use in FromBase36String and ToBase36String
const string kBase36Digits= "0123456789abcdefghijklmnopqrstuvwxyz";
static readonly double kBase36CharsLengthDivisor= Math.Log(kBase36Digits.Length, 2);
static readonly BigInteger kBigInt36= new BigInteger(36);
// assumes the input 'chars' is in big-endian ordering, MSB->LSB
static byte[] FromBase36String(string chars)
{
var bi= new BigInteger();
for (int x= 0; x < chars.Length; x++)
{
int i= kBase36Digits.IndexOf(chars[x]);
if (i < 0) return null; // invalid character
bi *= kBigInt36;
bi += i;
}
return bi.ToByteArray();
}
// characters returned are in big-endian ordering, MSB->LSB
static string ToBase36String(byte[] bytes)
{
// Estimate the result's length so we don't waste time realloc'ing
int result_length= (int)
Math.Ceiling(bytes.Length * kByteBitCount / kBase36CharsLengthDivisor);
// We use a List so we don't have to CopyTo a StringBuilder's characters
// to a char[], only to then Array.Reverse it later
var result= new System.Collections.Generic.List<char>(result_length);
var dividend= new BigInteger(bytes);
// IsZero's computation is less complex than evaluating "dividend > 0"
// which invokes BigInteger.CompareTo(BigInteger)
while (!dividend.IsZero)
{
BigInteger remainder;
dividend= BigInteger.DivRem(dividend, kBigInt36, out remainder);
int digit_index= Math.Abs((int)remainder);
result.Add(kBase36Digits[digit_index]);
}
// orientate the characters in big-endian ordering
result.Reverse();
// ToArray will also trim the excess chars used in length prediction
return new string(result.ToArray());
}