如何从数字生成最短的数据库标识符名称?
How do I generate the shortest database identifier names from a number?
因为我正在编写生成 SQL 的软件,其中可能包含大量参数并将 SQL 记录到磁盘上,所以我有一个不常见的要求(也许更多的是出于好奇):生成最短的可能的唯一参数名称。
参数名称遵循标识符命名规则,通常是:
- 第一个字符是字母
- 后续字符可以是字母数字或某些其他字符,例如下划线。
- 几乎任何东西都可以使用引号(忽略 -- 引号标识符总共至少三个字符,例如
[_]
)
SQL生成代码知道总共有多少个标识符,因此可以根据整数生成名称。
这最终比我预期的更难,而且解决方案也不那么优雅。
我对无效值进行了硬编码(从 0
开始),因为它们很少,而且我为推导它们所做的每一次尝试最终都变得复杂而缓慢。我将不胜感激有关如何使它更优雅的想法。我也会 post 在 CodeReview 上。
大多数数据库支持少于 2^16 个参数(实际使用一个荒谬的数字),但在处理大于 35027(也荒谬)的数字时,100 万是一个很好的强制停止点。
public static String intToDatabaseIdentifier(int number)
{
if(number < 0 || number > 1000000)
throw new ArgumentOutOfRangeException("number");
if(number > 25 && number <= 25 + 10) // Skip 0-9 (modified base 36)
number += 10;
if(number > 971 && number <= 971 + 360) // Skip 0a-09 (modified base 36)
number += 360;
if(number > 35027 && number <= 35027 + 12960) // Skip 0aa-099 (modified base 36)
number += 12960;
var stack = new Stack<char>();
// Base 36 starting with letters rather than numbers
const string characters = "abcdefghijklmnopqrstuvwxyz0123456789";
while(number >= 0) {
stack.Push(characters[number % 36]);
number = number / 36 - 1;
}
return new String(stack.ToArray());
}
以 0 开头的结果:
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
aa ab ac ad ae af ag ah ai aj aa ab ac ad ae af ag ah ai aj ak al am an ao
ap aq ar as at au av aw ax ay az a0 a1...
上面的代码会产生冲突。修复了没有碰撞和幻数的代码。
public static String intToDatabaseIdentifier(int number)
{
const string abcFirst = "abcdefghijklmnopqrstuvwxyz";
const string abcFull = "abcdefghijklmnopqrstuvwxyz0123456789";
if (number < 0 || number > 1000000)
throw new ArgumentOutOfRangeException("number");
var stack = new Stack<char>();
//Get first symbol. We will later reverse string. So last - will be first.
stack.Push(abcFirst[number % abcFirst.Length]);
number = number / abcFirst.Length;
//Collect remaining part
while (number > 0)
{
int index = (number - 1) % abcFull.Length;
stack.Push(abcFull[index]);
number = (number - index) / abcFull.Length;
}
//Reversing to guarantee first non numeric.
return new String(stack.Reverse().ToArray());
}
Timur Mannapov 的回答产生的结果类似于我的一些其他尝试(除了他的结果没有评论中指出的问题),因为进展不是人们所期望的,例如aa, ba, ca
而不是 aa, ab, ac
:
(调用 String.Concat(ToParamName(i))
)
// Starts with aa, ba, ba... instead of a, b, c. Probably wouldn't be hard
// to fix but I abandoned this method because it's annoying to call using
// string.Concat(...)
public static IEnumerable<char> ToParamName(int number) {
const string characters = "abcdefghijklmnopqrstuvwxyz0123456789";
yield return characters[number % 26];
number = number / 26;
do {
yield return characters[number % 36];
number = number / 36 - 1;
} while(number >= 0);
}
// Starts with a, b, c...aa, ba, ba but has collisions starting around 960
public static IEnumerable<char> ToParamName(int number) {
const string characters = "abcdefghijklmnopqrstuvwxyz0123456789";
yield return characters[number % 26];
number = number / 26;
while(number > 0) {
yield return characters[number % 36];
number = number / 36 - 1;
}
}
我更喜欢以更自然的顺序返回结果,例如 a..z, aa, ab, ac...a9
(嘿,我并没有声称我是纯粹实用的),但我忘了在原来的 post. Timur 的回答涵盖了所有原始要求,因此我将其标记为正确。
我会 +1 一个产生描述结果的答案。
因为我正在编写生成 SQL 的软件,其中可能包含大量参数并将 SQL 记录到磁盘上,所以我有一个不常见的要求(也许更多的是出于好奇):生成最短的可能的唯一参数名称。
参数名称遵循标识符命名规则,通常是:
- 第一个字符是字母
- 后续字符可以是字母数字或某些其他字符,例如下划线。
- 几乎任何东西都可以使用引号(忽略 -- 引号标识符总共至少三个字符,例如
[_]
)
SQL生成代码知道总共有多少个标识符,因此可以根据整数生成名称。
这最终比我预期的更难,而且解决方案也不那么优雅。
我对无效值进行了硬编码(从 0
开始),因为它们很少,而且我为推导它们所做的每一次尝试最终都变得复杂而缓慢。我将不胜感激有关如何使它更优雅的想法。我也会 post 在 CodeReview 上。
大多数数据库支持少于 2^16 个参数(实际使用一个荒谬的数字),但在处理大于 35027(也荒谬)的数字时,100 万是一个很好的强制停止点。
public static String intToDatabaseIdentifier(int number)
{
if(number < 0 || number > 1000000)
throw new ArgumentOutOfRangeException("number");
if(number > 25 && number <= 25 + 10) // Skip 0-9 (modified base 36)
number += 10;
if(number > 971 && number <= 971 + 360) // Skip 0a-09 (modified base 36)
number += 360;
if(number > 35027 && number <= 35027 + 12960) // Skip 0aa-099 (modified base 36)
number += 12960;
var stack = new Stack<char>();
// Base 36 starting with letters rather than numbers
const string characters = "abcdefghijklmnopqrstuvwxyz0123456789";
while(number >= 0) {
stack.Push(characters[number % 36]);
number = number / 36 - 1;
}
return new String(stack.ToArray());
}
以 0 开头的结果:
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
aa ab ac ad ae af ag ah ai aj aa ab ac ad ae af ag ah ai aj ak al am an ao
ap aq ar as at au av aw ax ay az a0 a1...
上面的代码会产生冲突。修复了没有碰撞和幻数的代码。
public static String intToDatabaseIdentifier(int number)
{
const string abcFirst = "abcdefghijklmnopqrstuvwxyz";
const string abcFull = "abcdefghijklmnopqrstuvwxyz0123456789";
if (number < 0 || number > 1000000)
throw new ArgumentOutOfRangeException("number");
var stack = new Stack<char>();
//Get first symbol. We will later reverse string. So last - will be first.
stack.Push(abcFirst[number % abcFirst.Length]);
number = number / abcFirst.Length;
//Collect remaining part
while (number > 0)
{
int index = (number - 1) % abcFull.Length;
stack.Push(abcFull[index]);
number = (number - index) / abcFull.Length;
}
//Reversing to guarantee first non numeric.
return new String(stack.Reverse().ToArray());
}
Timur Mannapov 的回答产生的结果类似于我的一些其他尝试(除了他的结果没有评论中指出的问题),因为进展不是人们所期望的,例如aa, ba, ca
而不是 aa, ab, ac
:
(调用 String.Concat(ToParamName(i))
)
// Starts with aa, ba, ba... instead of a, b, c. Probably wouldn't be hard
// to fix but I abandoned this method because it's annoying to call using
// string.Concat(...)
public static IEnumerable<char> ToParamName(int number) {
const string characters = "abcdefghijklmnopqrstuvwxyz0123456789";
yield return characters[number % 26];
number = number / 26;
do {
yield return characters[number % 36];
number = number / 36 - 1;
} while(number >= 0);
}
// Starts with a, b, c...aa, ba, ba but has collisions starting around 960
public static IEnumerable<char> ToParamName(int number) {
const string characters = "abcdefghijklmnopqrstuvwxyz0123456789";
yield return characters[number % 26];
number = number / 26;
while(number > 0) {
yield return characters[number % 36];
number = number / 36 - 1;
}
}
我更喜欢以更自然的顺序返回结果,例如 a..z, aa, ab, ac...a9
(嘿,我并没有声称我是纯粹实用的),但我忘了在原来的 post. Timur 的回答涵盖了所有原始要求,因此我将其标记为正确。
我会 +1 一个产生描述结果的答案。