首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >解析命令行

解析命令行
EN

Code Review用户
提问于 2015-11-18 03:28:54
回答 1查看 470关注 0票数 4

我编写了一个对象,它允许解析和序列化命令行。我不认为这是做了,但这是它的开始。我知道还有其他类似的实现,但它们要么太原始,要么太重。这是一种在Windows中简化解析和序列化命令行的尝试,包括应用程序、要打开的任何文件(S)以及任何其他参数。

我不能使用内置param开关的原因是,我需要能够将字符串从一个应用程序实例传送到另一个应用程序实例,同时只执行应用程序的一个实例。例如,假设已经打开了一个应用程序实例,并且用户选择打开一个现有文件,并提供特定的参数来控制其打开的方式。新实例将检测已经打开的实例,并在终止自身之前将命令行转发给它。然后,现有实例使用此解析器接收并处理命令行。

在我进一步研究这个物体之前,你看到它有什么严重的问题吗?

代码语言:javascript
复制
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
    //Module filename is wrapped in ""
    Delete(Str, 1, 1);
    P:= Pos('"', Str);
    Tmp:= Copy(Str, 1, P-1);
    Delete(Str, 1, P);
    FModuleFilename:= Tmp;
  end else begin
    //Module filename is not wrapped in ""
    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
    //Open filename is wrapped in ""
    Delete(Str, 1, 1);
    P:= Pos('"', Str);
    Tmp:= Copy(Str, 1, P-1);
    Delete(Str, 1, P);
    FOpenFilename:= Tmp;
  end else begin
    //Open filename is not wrapped in ""
    P:= Pos('-', Str);
    if P < 1 then
      P:= Pos('/', 'Str');
    if P < 1 then begin
      //Param does not have switch name
      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
      //Param switch prefix found
      Delete(Str, 1, 1);
      P:= Pos(' ', Str);
      Tmp:= Trim(Copy(Str, 1, P-1)); //Switch name
      Delete(Str, 1, P);
      Cmd:= Tmp;
      Str:= Trim(Str) + ' ';

      if (Pos('-', Str) <> 1) and  (Pos('/', Str) <> 1) then begin
        //This parameter has a value associated with it
        P:= Pos('"', Str);
        if P = 1 then begin
          //Value is wrapped in ""
          Delete(Str, 1, 1);
          P:= Pos('"', Str);
          Tmp:= Copy(Str, 1, P-1);
          Delete(Str, 1, P);
        end else begin
          //Value is not wrapped in ""
          P:= Pos(' ', Str);
          Tmp:= Copy(Str, 1, P-1);
          Delete(Str, 1, P);
        end;
        Val:= Tmp;
      end else begin
        Val:= '';
      end;
      //If blank, add space to ensure at least name gets added
      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.

注意:要检查的主要过程是TCmdLine.SetAsString

示例使用

代码语言:javascript
复制
CmdLine.AsString := '"C:\MyApp.exe" "C:\SomeFile.txt" -n -o "Some Value With Spaces" -f SomeOtherValueWithNoSpaces -p';

结果

  • ModuleFilename = C:\MyApp.exe
  • OpenFilename = C:\SomeFile.txt
  • Param n = 空白
  • Param o = Some Value With Spaces
  • Param f = SomeOtherValueWithNoSpaces
  • Param p = 空白
EN

回答 1

Code Review用户

发布于 2015-11-19 18:41:30

我认为这是可行的,但我认为您可以编写一个类,以便使用它的代码更具可读性。为什么不把它缩小到三个功能-

IsArg (如果存在开关,则返回true,否则为false)

GetArg (取一个开关,返回值(如果有的话)

GetDelimitedArg (接受一个开关和分隔符,返回一个数组结果)

示例:

代码语言:javascript
复制
unit CLArgParser;
//this class makes it easier to parse command line arguments
interface

uses
  Classes;

type
  strarr = array of string;

type
  TCLArgParser = class
  private
    FPermitTags : array of string;
    FTrimAll: boolean;
  public
    function IsArg(argtag : string) : boolean;
    function GetArg(argtag : string) : string;
    function GetDelimtedArg(argtag, delimiter : string) : TStringList;
    constructor Create(ArgTags : array of string); overload;
    constructor Create; overload;

    property TrimAll: boolean read FTrimAll write FTrimAll;
  end;

implementation

uses
  SysUtils;

const
  cDefaultTags : array[0..1] of string =  ('-','/');

constructor TCLArgParser.Create(ArgTags : array of string);
var i : integer;
begin
  try
    SetLength(FPermitTags,High(ArgTags)+1);
    for i := 0 to High(ArgTags) do begin
      FPermitTags[i] := ArgTags[i];
    end;  //for i
  except on e : exception do
    raise;
  end;  //try-except
end;

constructor TCLArgParser.Create;
begin
  FTrimAll := False;  //default value
  inherited Create;
  Create(cDefaultTags);
end;

function TCLArgParser.GetArg(argtag: string): string;
var i,j,n : integer;
begin
  try
    Result := '';
    n := High(FPermitTags);

    for i := 1 to ParamCount do
      for j := 0 to n do
        if Uppercase(ParamStr(i)) = (FPermitTags[j] + Uppercase(argtag)) then
          Result := ParamStr(i+1);

    if FTrimAll then begin
      Result := Trim(Result);
    end;
  except on e : exception do
    raise;
  end;  //try-except
end;

function TCLArgParser.GetDelimtedArg(argtag, delimiter: string): TStringList;
var i : integer;
    argval, tmp : string;
begin
  try
    Result := TStringList.Create;
    argval := GetArg(argtag);

    for i := 1 to Length(argval) do begin
      if ((i = Length(argval)) or ((argval[i] = delimiter) and (tmp <> '')))
      then begin
        if i = Length(argval) then begin
          tmp := tmp + argval[i];
          if FTrimAll then begin
            tmp := Trim(tmp);
          end;
        end;
        Result.Add(tmp);
        tmp := '';
      end  //if we found a delimted value
      else begin
        tmp := tmp + argval[i];
      end;  //else we just keep looking
    end;  //for ea. character

  except on e : exception do
    raise;
  end;  //try-except
end;

function TCLArgParser.IsArg(argtag: string): boolean;
var i,j,n : integer;
begin
  try
    Result := False;
    n := High(FPermitTags);

    for i := 1 to ParamCount do begin
      for j := 0 to n do begin
        if Uppercase(ParamStr(i)) = (FPermitTags[j] + Uppercase(argtag))
        then begin
          Result := True;
          Exit;
        end;  //if we found it
      end;  //for j
    end;  //for i
  except on e : exception do
    raise;
  end;  //try-except
end;

end.
票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/111076

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档