解析命令行参数(Free Pascal)

本文详细介绍了如何使用FreePascal的uGetOpt单元来解析命令行参数,支持短选项和长选项,以及自定义参数处理。通过示例展示了如何设置回调函数处理不同类型的选项和值。

解析命令行参数(Free Pascal)

{ ======================================================================
## 功能介绍

  解析命令行参数,并调用'回调函数'处理各个选项

  单独的 '-'  表示下一个参数为普通参数。
  单独的 '--' 表示后续参数都为普通参数。

## 举例说明

  cmd -  -file1 -f file2   // -file1 被识别为普通参数,-f 被识别为短选项
  cmd -- -file1 -f file2   // -file1 和 -f 都被识别为普通参数

## 用法演示

```pascal
// 定义回调函数(Key 是解析出来的选项名,Value 是解析出来的选项值)
function OptHandler(const Key, Value: String; Data: Pointer): Boolean;
begin
  case Key of
    'a', 'aa': WriteLn(Key, ' = ', Value);  // -a --aa 可以有选项值
    'b', 'bb': if Value = '' then           // -b --bb 可以有选项值
                 WriteLn('参数 ', Key, ' 必须指定选项值')
               else
                 WriteLn(Key, ' = ', Value);
    'c', 'cc': WriteLn(Key, ' 没有选项值');   // -c --cc 不能有选项值
    ''       : WriteLn('普通参数:', Value);  // 不以 - 开头的普通参数
    else       WriteLn('无效选项:', Key);    // 以   - 开头的其它选项
  end;

  // 返回 True 表示继续解析,返回 False 表示中止解析
  Result := True;
end;


// 将上面创建的回调函数传递给 GetOpt 函数,开始解析
procedure Test();
begin
  // 这里的 'a b aa bb' 是指 -a、-b、--aa、--bb 必须提供选项值
  // 其它选项不读取选项值
  if not GetOpt(Argc - 1, Argv + 1, 'a b aa bb', @OptHandler) then
    WriteLn(StdErr, '命令行参数解析失败');
end;


// 可以使用下面的 Test 函数配合上面的 OptHandler 进行测试
procedure Test();
var
  Args: array of String;
begin
  WriteLn(#10'----- 各种参数测试 -----'#10);
  Args := ['-aHello', '--aa', 'World', '-b', '', '--bb', '-', '-c', '--cc', '--', '-d', '--dd', 'hello'];
  GetOpt(Length(Args), PPChar(Args), 'a b aa bb', @OptHandler);

  WriteLn(#10'----- 缺少选项值测试 -----'#10);
  Args := ['-a', '--aa', '-b', '--bb'];
  GetOpt(Length(Args), PPChar(Args), 'a b aa bb', @OptHandler);

  WriteLn(#10'----- 选项名太短测试 -----'#10);
  Args := ['-aHello', '--a', 'World'];
  GetOpt(Length(Args), PPChar(Args), 'a b aa bb', @OptHandler);
end;
```
====================================================================== }

type

  // 用于处理单个命令行选项的回调函数
  TOptHandler = function(const Key, Value: String; Data: Pointer): Boolean;

{ ----------------------------------------------------------------------
功能:循环解析命令行参数并调用回调函数处理解析结果。
参数:Argc       参数数量
      Argv       参数列表,要求以 #0 结束(即要求 Argv[Argc] 为 #0)
      ParamOpts  允许接受选项值的选项列表,以空格分隔,比如 `n f name file`
      OptHandler 用来处理各个选项的回调函数
      CustomData 传递给回调函数的自定义数据
返回:全部解析完毕则返回 True,出错或中止则返回 False。
---------------------------------------------------------------------- }
function GetOpt(Argc: Integer; Argv: PPChar; ParamOpts: String;
  OptHandler: TOptHandler; CustomData: Pointer = nil): Boolean;
var
  Index  : Integer;    // 当前正在处理的命令行参数索引
  Arg    : PChar;      // 当前正在处理的命令行参数指针
  Key    : String;     // 当前解析出的选项名
  Value  : String;     // 当前解析出的选项值或普通参数
  Normal : Integer;    // 当前参数是否被视为普通参数,1 表示仅一次,2 表示后续全部

  // 读取下一个参数
  procedure NextArg();
  begin
    if Index <= Argc then
    begin
      Arg := Argv[Index];
      Inc(Index);
    end;
  end;

  // 调用回调函数,处理当前解析出来的选项名和选项值
  function HandleOpt(): Boolean;
  begin
    Result := OptHandler(Key, Value, CustomData);
    Key   := '';
    Value := '';
  end;

begin
  if Argc = 0 then
    Exit(True);

  Result := False;

  if OptHandler = nil then
    Exit;

  // 前后加空格,便于查找选项名
  ParamOpts := ' ' + ParamOpts + ' ';

  Index  := 0;
  Arg    := nil;
  Key    := '';
  Value  := '';
  Normal := 0;

  // 读取第一个选项,结果存入 Arg
  NextArg();

  while Index <= Argc do
  begin
    // 参数不以 - 开头,或参数前面指定了单独的 - 或 -- 选项,
    // 处理'普通参数'或'选项值'。
    if (Arg = nil) or (Arg^ <> '-') or (Normal <> 0) then
    begin
      Value := Arg;

      if not HandleOpt() then
        Exit;

      NextArg();

      if Normal = 1 then
        Normal := 0;
    end
    else
    // 参数以 - 开头
    begin
      // 单独的 - 参数
      if Arg[1] = #0 then
        Normal := 1
      else
      // - 之后跟随有内容
      begin
        // 上一个选项尚未处理完(因为需要选项值)
        // 这里没有遇到选项值,反而遇到另一个选项名,则使用空值作为选项值
        if Key <> '' then
        begin
          if not HandleOpt() then
            Exit;
          // 进入下一轮继续处理当前选项名
          Continue;
        end
        else
        // 以 -- 开头
        if Arg[1] = '-' then
        begin
          // 单独的 -- 参数
          if Arg[2] = #0 then
            Normal := 2
          else
          // --Key 长选项
          begin
            // 跳过 --
            Inc(Arg, 2);
            // 读取选项名
            Key := Arg;
            // 选项名太短,传递给 OptHandler 作为非法选项处理
            if Length(Key) < 2 then
              Key := '--' + Key;
            // 判断是否需要选项值
            if Pos(' ' + Key + ' ', ParamOpts) = 0 then
            begin
              // 如果不需要选项值,则处理当前选项名
              if not HandleOpt() then
                Exit;
            end;
          end;
        end
        else
        // -Key 短选项
        begin
          // 跳过 -
          Inc(Arg);
          // 循环解析连续的短选项名
          while Arg^ <> #0 do
          begin
            // 读取一个短选项名
            Key := Arg^;
            Inc(Arg);
            // 判断是否需要选项值
            if Pos(' ' + Key + ' ', ParamOpts) = 0 then
            begin
              // 如果不需要选项值,则处理当前选项名
              if not HandleOpt() then
                Exit;
            end
            // 如果需要选项值,则跳出内循环,在下一轮外循环处理选项值
            else
              Break;
          end;
          // 如果当前短选项后面还紧随有字符串,则该字符串可以作为选项值使用,不需要 NextArg()
          if Arg^ <> #0 then
            Continue;
        end;
      end;
      NextArg();
    end;
  end;

  // 如果最后一个选项需要选项值,但未提供,则使用空值作为选项值
  if (Key <> '') and not HandleOpt() then
    Exit;

  Result := True;
end;
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值