【问题标题】:Generate random password in Delphi在 Delphi 中生成随机密码
【发布时间】:2021-03-05 10:01:57
【问题描述】:

我有一个生成随机密码的函数:

function GeneratePassword(ALength: Integer; Mode: TPasswordMode): string;
const
  cLower   = 'abcdefghijklmnopqrstuvwxyz';
  cUpper   = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  cNumbers = '0123456789';
  cExtra   = '_.';
var
  i : Integer;
  S : string;
  iM: BYTE;
begin
  if Mode = [] then Exit;
  i := 0;
  Randomize;
  while (i < ALength)  do
  begin
    iM := RANDOM(4);
    case iM of
      0: if (pmLower in Mode) then begin
            S := S + cLower[1+RANDOM(Length(cLower))];
           Inc(i);
          end;
     1: if (pmUpper in Mode) then begin
           S := S + cUpper[1+RANDOM(Length(cUpper))];
          Inc(i);
         end;
      2: if (pmNumbers in Mode) then begin
           S := S + cNumbers[1+RANDOM(Length(cNumbers))];
           Inc(i);
          end;
      3: if (pmExtra in Mode) then begin
           S := S + cExtra[1+RANDOM(Length(cExtra))];
           Inc(i);
         end;
    end;
  end;
  Result := S;
end;

如何使这个函数使大写字母和特殊字符只出现一次,但总是出现?有时我生成密码时没有大写字母或特殊字符。

【问题讨论】:

  • 或者:绘制ALength-2 common chars;画一个大写字母并将其插入随机位置;绘制一个特殊字符并将其插入到随机位置。
  • 另一种方法:生成后,您现在可以扫描结果,如果缺少字符类型(大写字母或特殊字符),则在随机位置显式添加一个。
  • 或者先添加所需的单个字符,然后添加其余字符,最后随机打乱结果。
  • 就个人而言,这里提出的所有想法@dsm 似乎是最好的。不过要小心使用适当的随机播放,这是之前很多问题的主题。
  • @David 插入比随机播放结果更快。

标签: delphi


【解决方案1】:

要确保有一个特殊字符和一个大写字母,您可以这样做:

function GeneratePassword(ALength: Integer; Mode: TPasswordModes): string;
const
  cLower   = 'abcdefghijklmnopqrstuvwxyz';
  cUpper   = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  cNumbers = '0123456789';
  cExtra   = '_.';
var
  iM: Byte;
  i: integer;
begin
  if Mode = [] then Exit;

  Result := '';
  i := 0;

  if pmUpper in Mode then
    Inc(i);

  if pmExtra in Mode then
    Inc(i);

  // add lower case and/or number
  while Result.Length < (ALength - i)  do
  begin
    iM := Random(2);
    case iM of
      0: if (pmLower in Mode) then begin
           Result := Result + cLower[1 + Random(Length(cLower))];
         end;
      1: if (pmNumbers in Mode) then begin
           Result := Result + cNumbers[1 + Random(Length(cNumbers))];
         end;
    end;
  end;

  // add uppercase and/or extra
  if i > 0 then
  begin
    if pmUpper in Mode then
      Result := Result.Insert(1 + Random(Length(Result)), cUpper[1 + Random(Length(cUpper))]);

    if pmExtra in Mode then
      Result := Result.Insert(1 + Random(Length(Result)), cExtra[1 + Random(Length(cExtra))]);
  end;
end;

【讨论】:

  • 不要像那样调用 Randomize。仅在启动时一次。尝试快速连续调用此函数两次,看看如何获​​得相同的输出。
  • 那是原始代码的一部分。我没写完。
  • 我调用了这个函数100次就ok了,而不是分发-1,到处写同样的评论,解释并给出更好的解决方案。
  • 问题中的代码也是错误的,Randomize 的文档完全按照我所说的进行了说明。此外,为 PRNG 播种是一个众所周知的话题。
  • 如果你不熟悉,想象一下连续两次调用 Randomize 会导致 RNG 使用相同的种子初始化,因为内部时钟没有改变。
【解决方案2】:
type
  TPasswordMode = (pmLower, pmUpper, pmNumbers, pmExtra);
  TPasswordModes = set of TPasswordMode;

implementation

function GeneratePassword(ALength: Integer; Mode: TPasswordModes): string;
const
  cLower   = 'abcdefghijklmnopqrstuvwxyz';
  cUpper   = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  cNumbers = '0123456789';
  cExtra   = '_.';
var
  i : Integer;
  S : string;
  iM: BYTE;
begin
  if Mode = [] then Exit;
  i := 0;
  Randomize;
  while (i < ALength)  do
  begin
    iM := RANDOM(4);
    case iM of
      0: if (pmLower in Mode) then begin
           S := S + cLower[1+RANDOM(Length(cLower))];
           Inc(i);
         end;
      1: if (pmUpper in Mode) then begin
           S := S + cUpper[1+RANDOM(Length(cUpper))];
           Inc(i);
           Mode := Mode - [pmUpper]; // This I added
         end;
      2: if (pmNumbers in Mode) then begin
           S := S + cNumbers[1+RANDOM(Length(cNumbers))];
           Inc(i);
         end;
      3: if (pmExtra in Mode) then begin
           S := S + cExtra[1+RANDOM(Length(cExtra))];
           Inc(i);
           Mode := Mode - [pmExtra];  // This I added
         end;
    end;
  end;
  Result := S;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage(GeneratePassword(10,[pmLower,pmUpper,pmNumbers,pmExtra]));
end;

这不是一个完整的解决方案,但有了它,您至少会在使用 Upper 和 Extra 后立即从要求中删除它们。如果需要,您现在检查最后是否曾经添加过它们,然后在需要时添加它们。


编辑:

当我输入上述内容时,我很着急。您只需要最后检查生成的密码是否包含大写和额外字符。如果没有,您仍然需要添加它们,因为这是您的要求之一。

【讨论】:

  • 我不明白你的最后一句话。我可以猜到你可能想说什么,但请尝试更好地表述它。
  • @TomBrunberg :-)。当我输入上面的内容时,我很着急。他只需要最后检查生成的密码是否包含Upper 和Extra 字符。如果不是,他仍然需要添加它们,因为这是他的要求之一。
  • 不要像那样调用 Randomize。仅在启动时一次。尝试快速连续调用此函数两次,看看如何获​​得相同的输出。
【解决方案3】:

这是一个示例,首先确保所有额外的模式都被填充,其余的。它用空格预填充 Result,然后用随机字符替换,直到所有空格都被替换。

function GetRandomEmptyPos(const aStr: string): integer; inline;
begin
  // find random empty position
  repeat
    Result := Random(Length(aStr)) + 1;
  until aStr[Result] = ' ';
end;

function GeneratePassword2(aLength: Integer; aModes: TPasswordModes): string;
const
  cLower   = 'abcdefghijklmnopqrstuvwxyz';
  cUpper   = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  cNumbers = '0123456789';
  cExtra   = '_.';
var
  i,vPos: integer;
  vMode: TPasswordMode;
begin
  if (aLength = 0) or (aModes = []) then Exit;
  Randomize;

  // Prefill Result with empty spaces
  Result := StringOfChar(' ', aLength);

  // Add extra characters at random places
  for vMode in aModes do
  begin
    vPos := GetRandomEmptyPos(Result);
    case vMode of
      pmLower:    Result[vPos] := cLower[Random(Length(cLower)) + 1];
      pmUpper:    Result[vPos] := cUpper[Random(Length(cUpper)) + 1];
      pmNumbers:  Result[vPos] := cNumbers[Random(Length(cNumbers)) + 1];
      pmExtra:    Result[vPos] := cExtra[Random(Length(cExtra)) + 1];
    end;
  end;

  // Add random char on emtpy spaces
  for i := 1 to Result.Length do
    if Result[i] = ' ' then
      Result[i] := String(cLower + cNumbers)[Random(Length(cLower) + Length(cNumbers)) + 1];
end;

【讨论】:

  • 他只想要一个额外的和一个鞋面的,用你的代码他可以得到超过1个
  • @Bosshoss 谢谢,我修复了随机字符的最终添加。
  • 不要像那样调用 Randomize。仅在启动时一次。尝试快速连续调用此函数两次,看看如何获​​得相同的输出。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-09-08
  • 2011-08-31
  • 2012-11-06
  • 1970-01-01
  • 2015-04-13
相关资源
最近更新 更多