5

当您阅读ParamStr()时,它由每个参数之间的空格分隔。但是,我看到许多命令行参数接受参数名称与其配对值之间的空格,同时也接受等号=,甚至不接受分隔符(仅以参数名称为前缀)或不接受值。

以下是可能的参数字符串的一些示例:

-name value
/name value
-name=value
/name=value
-namevalue
/namevalue
-name -nextname
/name /nextname
-name="value with spaces"
/name="value with spaces"

...ETC。

我想做的是两件事都相关...检查参数名称是否存在,并读取参数的值。例如...

if ParamExists('ParamName') then
  SomeString:= ParamValue('ParamName')
else
  SomeString:= 'SomeOtherString';

Delphi中有什么可以做到这一点吗?如果没有,我该怎么做?我在搜索时找到的所有内容都将我引向相同的基本示例:

for i := 0 to ParamCount do
  ShowMessage(ParamStr(i));

它还需要区分大小写。我正在寻找特别喜欢OSQL和类似的命令行工具使用的东西,其中“-s”可能与“-S”不同。

问题是,如果我使用空格作为分隔符,我不知道如何识别它何时是前一个参数的一部分,因为它用空格分隔它们。我该如何解决这个问题?

我相信这也有一个标准术语,它是命令行参数的常见格式。但我不知道如何使用 just 正确阅读它们ParamStr。它似乎ParamStr达不到它通常的用途。

需要说明的是,我不一定需要支持上面的每个例子——这些只是我以前见过的例子。

4

4 回答 4

5

ParamStr()(因此FindCmdLineSwitch())不够灵活,无法处理您展示的所有示例。您将不得不调用 Win32 APIGetCommandLine()函数并手动解析它。

于 2013-06-15T08:27:56.653 回答
1

具有讽刺意味的是,就在昨晚我为此写了一些东西,而就在不久前,我发现这得到了投票。这是我刚刚写的一个封装这个的类:

unit CmdLine;

(*
  Command Line Parser
  by Jerry Dodge

  Class: TCmdLine
  - Parses out a command line into individual name/value pairs
  - Concatenates name/value pairs into a command line string
  - Property "ModuleFilename" for the current executable path
  - Property "OpenFilename" for the file to be opened, if any
  - Default property "Values" to read/write name/value pairs
*)

interface

uses
  System.Classes, System.SysUtils;

type
  TCmdLine = class(TObject)
  private
    FItems: TStringList;
    FModuleFilename: String;
    FOpenFilename: String;
    function GetAsString: String;
    procedure SetAsString(const Value: String);
    procedure SetModuleFilename(const Value: String);
    procedure SetOpenFilename(const Value: String);
    function GetValue(const Name: String): String;
    procedure SetValue(const Name, Value: String);
    function GetName(const Index: Integer): String;
  public
    constructor Create;
    destructor Destroy; override;
    function Count: Integer;
    function Exists(const N: String; const IgnoreCase: Boolean = False): Boolean;
    property ModuleFilename: String read FModuleFilename write SetModuleFilename;
    property OpenFilename: String read FOpenFilename write SetOpenFilename;
    property AsString: String read GetAsString write SetAsString;
    property Names[const Index: Integer]: String read GetName;
    property Values[const Name: String]: String read GetValue write SetValue; default;
  end;

implementation

{ TCmdLine }

constructor TCmdLine.Create;
begin
  FItems:= TStringList.Create;
end;

destructor TCmdLine.Destroy;
begin
  FItems.Free;
  inherited;
end;

function TCmdLine.Count: Integer;
begin
  Result:= FItems.Count;
end;

function TCmdLine.Exists(const N: String; const IgnoreCase: Boolean = False): Boolean;
var
  X: Integer;
begin
  Result:= False;
  for X := 0 to FItems.Count-1 do begin
    if IgnoreCase then begin
      if SameText(N, FItems.Names[X]) then begin
        Result:= True;
        Break;
      end;
    end else begin
      if N = FItems.Names[X] then begin
        Result:= True;
        Break;
      end;
    end;
  end;
end;

procedure TCmdLine.SetModuleFilename(const Value: String);
begin
  FModuleFilename:= Value;
end;

procedure TCmdLine.SetOpenFilename(const Value: String);
begin
  FOpenFilename:= Value;
end;

function TCmdLine.GetValue(const Name: String): String;
begin
  Result:= FItems.Values[Name];
end;

procedure TCmdLine.SetValue(const Name, Value: String);
begin
  FItems.Values[Name]:= Value;
end;

function TCmdLine.GetAsString: String;
var
  X: Integer;
  Cmd: String;
  Val: String;
begin
  Result:= '"'+FModuleFilename+'"';
  if Trim(FOpenFilename) <> '' then
    Result:= Result + ' "'+FOpenFilename+'"';
  for X := 0 to FItems.Count-1 do begin
    Cmd:= FItems.Names[X];
    Val:= FItems.Values[Cmd];
    Result:= Result + ' -'+Cmd;
    if Trim(Val) <> '' then begin
      Result:= Result + ' ';
      if Pos(' ', Val) > 0 then
        Result:= Result + '"'+Val+'"'
      else
        Result:= Result + Val;
    end;
  end;
end;

function TCmdLine.GetName(const Index: Integer): String;
begin
  Result:= FItems.Names[Index];
end;

procedure TCmdLine.SetAsString(const Value: String);
var
  Str: String;
  Tmp: String;
  Cmd: String;
  Val: String;
  P: Integer;
begin
  FItems.Clear;
  FModuleFilename:= '';
  FOpenFilename:= '';
  Str:= Trim(Value) + ' ';

  //Extract module filename
  P:= Pos('"', Str);
  if P = 1 then begin
    Delete(Str, 1, 1);
    P:= Pos('"', Str);
    Tmp:= Copy(Str, 1, P-1);
    Delete(Str, 1, P);
    FModuleFilename:= Tmp;
  end else begin
    P:= Pos(' ', Str);
    Tmp:= Copy(Str, 1, P-1);
    Delete(Str, 1, P);
    FModuleFilename:= Tmp;
  end;

  Str:= Trim(Str) + ' ';

  //Extract open filename
  P:= Pos('"', Str);
  if P = 1 then begin
    Delete(Str, 1, 1);
    P:= Pos('"', Str);
    Tmp:= Copy(Str, 1, P-1);
    Delete(Str, 1, P);
    FOpenFilename:= Tmp;
  end else begin
    P:= Pos('-', Str);
    if P < 1 then
      P:= Pos('/', 'Str');
    if P < 1 then begin
      P:= Pos(' ', Str);
      Tmp:= Copy(Str, 1, P-1);
      Delete(Str, 1, P);
      FOpenFilename:= Tmp;
    end;
  end;

  Str:= Trim(Str) + ' ';

  //Extract remaining param switches/values
  while Length(Trim(Str)) > 0 do begin
    P:= Pos('-', Str);
    if P < 1 then
      P:= Pos('/', 'Str');
    if P > 0 then begin
      Delete(Str, 1, 1);
      P:= Pos(' ', Str);
      Tmp:= Trim(Copy(Str, 1, P-1));
      Delete(Str, 1, P);
      if Pos('"', Tmp) = 1 then begin
        Delete(Tmp, 1, 1);
        P:= Pos('"', Tmp);
        if P > 0 then
          Delete(Tmp, 1, 1);
      end;
      Cmd:= Tmp;
      Str:= Trim(Str) + ' ';
      if (Pos('-', Str) <> 1) and  (Pos('/', Str) <> 1) then begin
        P:= Pos('"', Str);
        if P = 1 then begin
          Delete(Str, 1, 1);
          P:= Pos('"', Str);
          Tmp:= Copy(Str, 1, P-1);
          Delete(Str, 1, P);
        end else begin
          P:= Pos(' ', Str);
          Tmp:= Copy(Str, 1, P-1);
          Delete(Str, 1, P);
        end;
        Val:= Tmp;
      end else begin
        Val:= '';
      end;
      if Val = '' then
        Val:= ' ';
      FItems.Values[Cmd]:= Val;
    end else begin
      Str:= '';
      raise Exception.Create('Command line parameters malformed ('+Str+')');
    end;
    Str:= Trim(Str) + ' ';
  end;
end;

end.
于 2015-11-16T18:05:10.877 回答
0

我只是重写了我所有提出的答案,以增加一些价值,谢谢您的评论:

function TForm1.ParamExists(sParamName: String;
  bIgnoreCase: boolean; equalchar : string = '='): Boolean;
begin
  if bIgnoreCase then
    Result := Pos(sParamName + equalChar, cmdLine) > 0
  else
    Result := AnsiPos(sParamName + equalChar, cmdLine) > 0;
end;

function TForm1.ParamValue(sParamName : String; bIgnoreCase : boolean = false;
                           equalchar : string = '='; delimiter : string = '"'): String;
var
  I : Integer;
  scmdLine : String;
begin
  Result := '';
  scmdLine := cmdLine;
  if bIgnoreCase then
    I := Pos(sParamName, scmdLine)
  else
    I := AnsiPos(sParamName, scmdLine);

  inc(I, Length(sParamName + equalchar));

  delete(scmdLine, 1, I-1);
  if pos(delimiter, scmdLine) = 1 then
  begin
    delete(scmdLine, 1, 1);
    Result := copy(scmdLine,1, pos(delimiter, scmdLine) -1);
  end
  else
    Result := copy(scmdLine,1, pos(' ', scmdLine));
end;

假设这个参数:

project1.exe -name1 value1 /name2 value2 -name3=value3 /name4=value4 -name5value5 /name6value6 -name7 -name8 /name9 /name91 -name10="Value 10" /name11="Value 11" 

例子:

ParamExists('-Name1', False, ' '); --> False
ParamExists('-Name1', True, ' '); --> True, ignore case
ParamExists('-name1', False, ' '); --> True
ParamExists('-name1', False, '='); --> False there are no a = after -name1 parameter
ParamExists('-name6', False, ''); --> True this parameter use the form -parameterValue 

参数值示例:

ParamValue('-name1', false, ' '); --> value1
ParamValue('/name2', false, ' '); --> value2
ParamValue('-name3'); --> value3, using default values
ParamValue('-name4'); --> value4, using default values
ParamValue('-name5', false, ''); --> value5
ParamValue('/name6', false, ''); --> value6

请注意,参数 -name7、-name8、/name9 和 /name91 必须评估为 ParamExists,因为没有为它们分配值。

ParamValue('-name10', false, '=', '"'); --> Value 10
same as:
ParamValue('-name10'); --> Value 10

ParamValue('/name11'); --> Value 11
于 2018-11-29T22:03:00.530 回答
0

我有同样的问题我做了这个功能它很简单而且非常有用

function GetCmdLineValue(CmdLine: string; Arg: string; Switch: Char; Separator: Char): string;
var
 ArgIndex: Integer;          // Index of Argument
 SepIndex: Integer;          // Index of Separator between Arg and Value ex "=" or ":"
 NextSwitchIndex: Integer;   // Index of the next Arg ex "-"
 LenghtValue: Integer;
begin
     ArgIndex:= CmdLine.IndexOf(Arg);
     SepIndex:= CmdLine.IndexOf(Separator, ArgIndex);
     NextSwitchIndex:= CmdLine.IndexOf(Switch, ArgIndex+1);

     { No Value found then Exit }

       // no separator found
     if (SepIndex = -1) or

       // the separator is located after the next switch
        ( SepIndex > NextSwitchIndex) and (NextSwitchIndex > -1) then Exit('');

     if NextSwitchIndex = -1 then   // No switch after Arg
        LenghtValue:= CmdLine.Length - SepIndex+2
     else
        LenghtValue:= NextSwitchIndex - SepIndex-1;

     Result:= Copy(CmdLine, SepIndex+2, LenghtValue).Trim;
end;

演示

在此处输入图像描述

用途:

procedure TForm1.Button1Click(Sender: TObject);
begin
     edt_Value.Text:= GetCmdLineValue(edt_CmdLine.Text, edt_Argum.Text, '-', '=');
end;
于 2021-06-05T11:21:03.857 回答