如何将一个数字转换为自定义2 char basex和back? (又名:如何进行Azure表属性压缩)
-
03-10-2019 - |
题
类似于一个人在十六进制中从0到f的计数,我有一系列我想从...计数的数字和字母...当我达到最大值时,我想在“ Tens”中再次开始“ 柱子。
我需要这以提高Azure表中的存储效率,并保持我的主要2型(以便我可以在Tinyurl中使用它们)。首先考虑仅允许这些字符作为属性名称,如有记录 这里. 。在下面的数组中,每个字符都根据Azure将其排序的方式定位。
public static string[] AzureChars = new string[]
{
"0","1","2","3","4","5","6","7","8","9","A",
"B","C","D","E","F","G","H","I",
"J","K","L","M","N","O","P","Q",
"R","S","T","U","V","W","X","Y",
"Z","a","b","c","d","e","f","g",
"h","i","j","k","l","m","n","o",
"p","q","r","s","t","u","v","w",
"x","y","z"
};
我的目标是使用2个字符串/ASCII字符来计数从字符串“ 00”来较低的“ ZZ”。
使用C#处理此概念的最佳方法是什么?
- 数组是要使用的正确对象吗?
- 我将如何将给定的字符(大写“ Y”)与数组中的位置相关联?
我只是在尝试这个想法。刚开始看来,这似乎是一个好东西,但是我没有看到有人考虑这样做。你怎么看?
解决方案
您的问题实际上是关于将数字转换为两个数字基数62数字。这是将正数转换为任意基础的一般代码段:
var n = 1234;
var baseNumber = 62;
var numberOfDigits = 2;
var digits = new Int32[numberOfDigits];
for (var i = 0; i < digits.Length; i += 1) {
digits[i] = n%baseNumber;
n /= baseNumber;
}
您必须将数字映射到字符和查找表或小型功能中,这是合适的。
对于您的特定问题,具有可变数字数字的附加功能,我会编写此代码:
var n = 123456;
var digitCount = 3;
var digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var number = String.Empty;
for (var i = 0; i < digitCount; ++i) {
number = digits[n%digits.Length] + number;
n /= digits.Length;
}
请注意,此代码将转换 0
进入 000
, 1
进入 001
等等,但是我认为这实际上就是您想要的。
要转换,您可以使用此代码:
var n = 0;
for (var i = 0; i < number.Length; ++i)
n = n*digits.Length + digits.IndexOf(number[i]);
这 String.IndexOf()
不是最有效的转换方法,但在大多数情况下应该可以。
请注意,如果您的原始数字大于可以存储在基本62号码中的最大数字,则转换后将会导致不同的数字。对于基准62中的3位数字,如果原始数字大于或等于 zzz = 62^3 - 1 = 238327
.
其他提示
由于数组的元素都是单个字符,因此您可以将其声明为字符数组:
public static char[] AzureChars = new char[]
{
'0', '1', '2', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u',
'v', 'w', 'x', 'y', 'z'
};
现在,您可以轻松编写一个返回全部集合的函数 n- 任何所需弦长的字符串 n. 。我的版本是递归的;如果您发现长字符串太慢,则可以优化它:
public static IEnumerable<string> AzureStrings(int desiredLength)
{
if (desiredLength == 0)
return new[] { "" };
return AzureChars.SelectMany(ch => AzureStrings(desiredLength - 1)
.Select(str => ch + str));
}
现在我们可以使用 Skip
和 Take
:
// Prints “5v, 5w, 5x, 5y, 5z, 60, 61, 62, 64, 65”
Console.WriteLine(string.Join(", ", AzureStrings(2).Skip(300).Take(10)));
// Prints “3721”
Console.WriteLine(AzureStrings(2).Count());
尽管这是在输出任何内容之前计算出前300个元素的事实,但对我来说它足够快。即使在这里的疯狂计算也需要不到一秒钟:
// Prints “5PkS, 5PkT, 5PkU, 5PkV, 5PkW, 5PkX, 5PkY, 5PkZ, 5Pka, 5Pkb”
Console.WriteLine(string.Join(", ", AzureStrings(4).Skip(1000000).Take(10)));
使用模量(并获取其余部分)
int i = AzureChars.Length;
int index = 62 //character to lookup;
string a = AzureChars[index % i];
获取字符的索引:
int index = Array.IndexOf(AzureChars, "Y");
像:
string text = "YY";
int index1 = Array.IndexOf(AzureChars, text[1].ToString());
int index2 = Array.IndexOf(AzureChars, text[0].ToString());
也许您应该使用chararray(char []),或者只是一个长字符串,例如:
static string AzureChars= "012456789.....qrstuvwxyz";
一起以清楚地表明:
static void Main(string[] args)
{
char[] b = AzureCharConverter.ToCharArray(522);
int i = AzureCharConverter.ToInteger(b);
}
public static class AzureCharConverter
{
private static readonly string _azureChars
= "012456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
public static int ToInteger(string chars)
{
int l = _azureChars.IndexOf(chars[0]);
int r = _azureChars.IndexOf(chars[1]);
return (l * _azureChars.Length) + r;
}
public static char[] ToCharArray(int value)
{
char l = _azureChars[value / _azureChars.Length];
char r = _azureChars[value % _azureChars.Length];
return new char[] { l, r };
}
}
如果输入alpha始终是两个数字,结果总是比3720要小