2009-08-14 83 views
6

我在我的应用程序中实现了URL缩短功能,以便为我的用户提供可在Twitter中使用的更短的替代URL。重点是独立于提供相同服务的缩短服务,并将其作为我的Web应用程序的一项功能。如何在C#中创建独特的随机字符序列?

什么是创建约6个字符的独特随机序列字符的最佳方式是什么?我计划使用它作为我的数据库中的项目的索引,这些项目将具有其他URL。

编辑:

此功能将在就业局的网站,在那里每一个新的招聘广告将获得与标题自定义URL加上较短的一个在Twitter上使用使用。也就是说,独特的6个字符组合的总数将会长时间绰绰有余。

回答

3

你真的需要“随机”,或将“独一无二”就足够了?

独特之处是非常简单 - 只需将URL到数据库中,并转换顺序编号为记录是由您选择的字符集为代表的碱基-n个。例如,如果您只想在序列中使用[A-Z],则将记录的id转换为基数为26的数字,其中A = 1,B = 2,... Z = 26。 algothithm是一个递归的div26/mod26,其中商是必需的字符,余数用于计算下一个字符。

然后检索URL时,您执行的反函数,这是基26号转换回小数。执行SELECT URL WHERE ID = decimal,即完成!

编辑:

private string alphabet = "abcdefghijklmnopqrstuvwxyz"; 
    // or whatever you want. Include more characters 
    // for more combinations and shorter URLs 

public string Encode(int databaseId) 
{ 
    string encodedValue = String.Empty; 

    while (databaseId > encodingBase) 
    { 
     int remainder; 
     encodedValue += alphabet[Math.DivRem(databaseId, alphabet.Length, 
      out remainder)-1].ToString(); 
     databaseId = remainder; 
    } 
    return encodedValue; 
} 

public int Decode(string code) 
{ 
    int returnValue; 

    for (int thisPosition = 0; thisPosition < code.Length; thisPosition++) 
    { 
     char thisCharacter = code[thisPosition]; 

     returnValue += alphabet.IndexOf(thisCharacter) * 
      Math.Pow(alphabet.Length, code.Length - thisPosition - 1); 
    } 
    return returnValue; 
} 
+0

感谢您的回答!我有一个问题:**什么是'encodingBase'?** – 2010-11-26 18:01:40

+0

@Maxim - 一年前很难记住,但我会想象它是编码字的长度;即对于像上面那样的base-26编码,那么它将是26.然而,我认为在代码中也存在一个错误!它应该是> 1 – 2010-11-28 10:30:56

3

制作独特序列的最简单方法是按顺序执行此操作,即:aaaaaa aaaaab aaaaac ...这些不一定是最漂亮的,但会保证第一个12230590463序列的唯一性(假设您使用az和AZ作为独特的人物)。如果你需要更多的URL,你需要添加第七个字符。

虽然它们不是随机序列。如果你制作的是随机的,只需选择48个随机字符,6次。但是,您需要检查现有的数据库是否存在“已使用”序列,因为您将更有可能发生冲突。

+0

它确实会导致更多的工作,并可能超时您最终使用的值越多。 – RiddlerDev 2009-08-14 01:09:23

+0

是的。 Sequential可以避免所有这些,但是最终会产生连续的值(可能会也可能不会很好)。对于Twitter网址,我只是使用seq。值,因为它确实无关紧要,它们真的是“随机的”,并且你将拥有一个巨大的唯一值序列,具有简单的代码 – 2009-08-14 16:32:16

+0

如果包含0-9作为字符集的一部分,那么你有56800235584独特的值(〜4.6x只是AZ,AZ)。我知道他刚才提到“角色”,但可能包括数字。如果你还加入了一些其他的字符,如* $%()等,那么它也会增加一些(至少是原来的9倍)。 – 2009-08-14 17:26:07

0

想想这更多这里是一个想法。

您可以从一个关键表开始,递增字符AAAAAA - ZZZZZZ。

然后在每次插入新URL时从该表中随机选择,并从可用密钥中删除。

想法?

对于随机选择尝试这种link

Select a random row with MySQL: 

SELECT column FROM table 
ORDER BY RAND() 
LIMIT 1 
Select a random row with PostgreSQL: 

SELECT column FROM table 
ORDER BY RANDOM() 
LIMIT 1 
Select a random row with Microsoft SQL Server: 

SELECT TOP 1 column FROM table 
ORDER BY NEWID() 
Select a random row with IBM DB2 

SELECT column, RAND() as IDX 
FROM table 
ORDER BY IDX FETCH FIRST 1 ROWS ONLY 
Thanks Tim 

Select a random record with Oracle: 

SELECT column FROM 
(SELECT column FROM table 
ORDER BY dbms_random.value) 
WHERE rownum = 1 
2

我会使用自动编号系统,并创建一个算法来生成密钥。即1 = a,2 = b,27 = aa等。

您可以使用数据库自​​动编号来确保您的URL是唯一的,并且可以计算可能位于数据库或业务层中的存储区中的URL ?

此外,您现在可以索引增量数字,这是便宜的和数据库的优化,这些将被使用和哈希作为主/外键,而不是一个可变长度的随机字符串。

1

随机生成器的有用性限于防止用户插入随机URL以找到他们不应该链接的东西。如果这不是你的目标,那么顺序ID应该可以正常工作。如果你不想让用户觉得他们使用的是“幼儿”技术(当他们看到他们的招聘广告是#000001),为什么不以某种任意价值开始序列呢?

0

而不是保留一个所有可能的值的表,只是保留一个你已经使用的值的表。使用随机函数生成6个随机值,1到26,从中产生字符串并将其保存在数组或表中。如果它已经存在,您可以(a)生成另一个字符串,或者(b)在表格中移动到下一个可用(缺少)的6字母字符串并使用该值。 (b)在表格填满时将更有效率。

0

继里德·科普塞的答案的想法,我提出以下代码:

class IDGetter 
{ 
    private StringID ID = new StringID(); 
    public string GetCurrentID() 
    { 
     string retStr = ""; 
     if (ID.char1 > 51) 
      id.char1 = 0; 
     if (ID.char2 > 51) 
      id.char2 = 0; 
     if (ID.char3 > 51) 
      id.char3 = 0; 
     if (ID.char4 > 51) 
      id.char4 = 0; 
     if (ID.char5 > 51) 
      id.char5 = 0; 
     if (ID.char6 > 51) 
      throw new Exception("the maximum number of id's has been reached"); 
     return ToIDChar(ID.char1) + ToIDChar(ID.char2) + ToIDChar(ID.char3) + ToIDChar(ID.char4) + ToIDChar(ID.char5) + ToIDChar(ID.char6) 
     id.char1++; 
    } 
    public void SetCurrentID(StringID id) //for setting the current ID from storage or resetting it or something 
    { 
     this.ID = id; 
    } 
    private const string alphabet = "abcdefghijklmopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 
    private static string ToIDChar(int number) 
    { 
     if (number > 51 || number < 0) 
     { 
      throw new InvalidArgumentException("the number passed in (" + number + ") must be between the range 0-51"); 
     } 
     return alphabet[number]; 
    } 
} 
public struct StringID 
{ 
    public int char1 = 0; 
    public int char2 = 0; 
    public int char3 = 0; 
    public int char4 = 0; 
    public int char5 = 0; 
    public int char6 = 0; 
} 

您可能要拿出存储当前ID的方法,但应该工作。

1

当你说出“独特的6个字符组合总数将绰绰有余了好久”为您随机生成你的因素进入birthday paradox你的计算?这通常是任何尝试在范围内创建随机ID的祸根,该范围只有一个数量级或更少,然后才是需要的预期范围。

要创建真正的随机编号,你需要创建生成新的随机值,检查是否该值已被使用,然后如果需要的话重复这个循环的循环。生日悖论意味着您很快就会达到许多生成的值已被使用的程度(尽管只占总范围的一小部分),这会导致程序随着时间的推移变得越来越慢,直到它花费数千尝试(和数据库查找)来生成每个ID。

我建议你去编码顺序ID的想法。为了避免用户能够简单地增加/减少URL中的值以“探索”,您可以使用组合位移和替代的有序字母列表(而不是1 = a,2 = b使用1 = t,2 = j等)。

0

我用它做了非常相似的事情。我不会担心它的速度,因为它将是一个很少使用的事件和表格。但是可以根据需要增加字符串。

/// Generates a string and checks for existance 
/// <returns>Non-existant string as ID</returns> 
public static string GetRandomNumbers(int numChars, string Type) 
{ 
    string result = string.Empty; 
    bool isUnique = false; 
    while (!isUnique) 
    { 
     //Build the string 
     result = MakeID(numChars); 
     //Check if unsued 
     isUnique = GetValueExists(result, Type); 
    } 
    return result; 
} 
/// Builds the string 
public static string MakeID(int numChars) 
{ 
    string random = string.Empty; 
    string[] chars = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" }; 
    Random rnd = new Random(); 
    for (int i = 0; i < numChars; i++) 
    { 
     random += chars[rnd.Next(0, 35)]; 
    } 
    return random; 
} 
/// Checks database tables based on type for existance, if exists then retry 
/// <returns>true or false</returns> 
private static bool GetValueExists(string value, string Type) 
{ 
    bool result = false; 
    string sql = ""; 
    if (Type == "URL") 
    { 
     sql = string.Format(@"IF EXISTS (SELECT COUNT(1) FROM myTable WHERE uniqueString = '{0}') 
     BEGIN 
      SELECT 1 
     END 
      ELSE 
      BEGIN 
      SELECT 0 
     END ", value); 
    } 
    //query the DB to see if it's in use 
    result = //ExecuteSQL 
    return result; 
}