官术网_书友最值得收藏!

  • Perl語言IC設計實踐
  • 滕家海編著
  • 2136字
  • 2022-02-08 17:37:59

1.4 繼續改進命令行參數

1.3節中,我們對參數的假設是過于嚴苛的,目的是降低代碼的復雜程度。

不同的選項可能有不同數量的參數值,有的只有一個,有的可能有多個。之前也提到過,我們有意不支持開關類(只有選項,而沒有對應的參數值)的選項。

本節,我們的目標是:

1)根據每個選項的不同要求,合理地存儲每個選項的參數值。

2)重復的選項被視為錯誤。

3)如果用戶輸入了錯誤的參數,程序會輸出相應的提示信息,并提前結束程序。

下面我們看看實際代碼:

代碼1-3 ch01/read_argument_v3.pl

 1 #!/usr/local/bin/perl
 2 
 3 my %rule_of_opt = (
 4   '-s' => {
 5             'perl_type' => 'scalar',
 6           },
 7   '-a' => {
 8             'perl_type' => 'array',
 9           }
10 );
11 
12 my ($opt, %value_of_opt) ;
13 for my $arg ( @ARGV ) {
14   if ( $arg =~ /^-/ ) {
15     $opt = $arg;
16     if ( exists $value_of_opt{$opt} ) {
17       print "Repeated option: $arg\n";
18       exit 1;
19     }
20     else {
21       @{ $value_of_opt{$opt} } = ();
22     }
23   }
24   elsif ( defined $opt ) {
25     push @{ $value_of_opt{$opt} }, $arg;
26   }
27   else {
28     print "Un-support option: $arg\n";
29     exit 1;
30   }
31 }
32 
33 for my $opt ( keys %value_of_opt ) {
34   if ( exists $rule_of_opt{$opt} ) {
35     if ( ${$rule_of_opt{$opt}}{'perl_type'} eq 'scalar') {
36       if ( @{ $value_of_opt{$opt} } != 1 ) {
37         print "Error: only one parameter is expected to '$opt'\n";
38         exit 1;
39       }
40     }
41     elsif ( ${$rule_of_opt{$opt}}{'perl_type'} eq 'array') {
42       if ( @{ $value_of_opt{$opt} } < 1 ) {
43         print "Error: one or more parameter is expected to '$opt'\n";
44         exit 1;
45       }
46     }
47     else {
48       print "Error: unknown 'perl_type' of '$opt'\n";
49       exit 1;
50     }
51   }
52   else {
53     print "Un-support option: '$opt'\n";
54     exit 1;
55   }
56 }
57 
58 for my $opt ( keys %value_of_opt ) {
59   print "$opt =>";
60   for my $pv ( @{ $value_of_opt{$opt} } ) {
61     print " $pv";
62   }
63   print "\n";
64 }
65 
66 exit 0;

這個程序分成4個部分:第1部分定義散列來描述選項的規則。第2部分讀取命令行參數。第3部分根據規則檢查命令行參數所在的散列。第4部分輸出命令行參數。

第3~10行,我們聲明并初始化了一個散列%rule_of_opt。它有兩個鍵-s和-a,每個鍵對應的不是單純的標量,而是一個嵌套的散列結構,由于這個散列結構沒有明確的名稱,我們也稱它為匿名散列。這兩個匿名散列各有一個鍵perl_type,且分別對應了一個字符串,分別是scalar和array。

第13~31行是一個for循環結構,它從@ARGV依次讀取參數,把參數值賦值給標量$arg。

第14~23行的if分支,來判斷$arg的類別。如果$arg是以短劃線開頭,緊跟數字、字母、下劃線的任意組合,那么就認為$arg是一個選項,并賦值給另一個標量($opt)存儲。

第16行中的exists判斷某個鍵是否存在于散列中。如果$key是散列%one_hash的一個鍵,則exists $one_hash{$key}返回真,否則返回假。如果第16行的$opt此前已經存在于%value_of_opt中,則表明現在是第2次讀取了,重復的選項是一種錯誤。

第21行,@{ $value_of_opt{$opt} }是一個數組,還記得我們之前介紹的Perl三種變量的起始字符嗎?@開頭的是數組,$value_of_opt{$opt}是散列%value_of_opt中的鍵$opt之所指。所以@{ $value_of_opt{$opt} }就是散列%value_of_opt中的鍵$opt指向的一個數組。我們把這個數組初始化成一個空的數組。

第24~26行,defined函數根據變量是否有值返回真假,如果有值,則返回真,否則返回假。如果$arg不符合第14行的if的條件要求,那么來到了elsif分支,此時需要判斷,此前是否已經定義該選項了。如果已經定義,則把當前的$arg添加到該數組@{ $value_of_opt{$opt} }的尾部。

第27~30行,如果上述兩個分支判斷都失敗,則來到了這個else分支,輸出錯信息,并終止程序,返回狀態1。什么情況下,會執行到這個分支呢?是的,可能你也想到了,就是程序的第一個參數不是選項時。例如:

./read_argument_v3.pl non-option -a something

程序會輸出:

Un-support option: non-option

第33~56行,遍歷并檢查已經存儲在散列%value_of_opt中的選項和對應的參數值。在這個for循環中,包含了3層if/else判斷。

第34行,判斷選項($opt)是否存在于散列%rule_of_opt。如果不存在,就來到了第52~55行,輸出信息,然后結束程序。如果存在,則繼續下一層的判斷。

第35~50行,判斷散列%rule_of_opt中的鍵$opt,所對應的perl_type是scalar還是array。如果都不是,則來到了47~50行,輸出信息,然后結束程序。eq操作符用于比對字符串,如果左右兩邊的字符串相同,則返回“真”,否則返回“假”。

第35~40行,處理${$rule_of_opt{$opt}}{'perl_type'}等于scalar的情形。處在if的條件中的@{ $value_of_opt{$opt} } != 1(見第36行)是Perl特有的靈活語法。“!=”是“不等于”的意思,它造就了一個“標量環境”,即假設左右兩邊都是標量。那么處在標量環境中的數組,意味著什么呢?它的意義很直觀,就是該數組的元素數量。如果數組@{ $value_of_opt{$opt} }的元素的個數不等于1,則此表達式返回“真”,否則返回“假”。

第41~46行,處理${$rule_of_opt{$opt}}{'perl_type'}等于array的情形,檢查此數組的元素數量,如果元素數量小于1,則被認為不符合程序規則,輸出提示信息,然后終止程序。

第58~64行,輸出所有選項和對應的參數值。

第66行,是程序的末尾,顯式地結束程序,并設置返回值為0(零)。

下面將詳細介紹本實例中出現的數據結構:數組的散列,散列的散列。

1.4.1 數組的散列

基本的散列如下:

%basic_hash = (
  "keyA" => "valueA",
  "keyB" => "valueB",
);

數組的散列則如下:

%array_of = (
  "keyA" =>  ["a1", "a2", …], 
  "keyB" =>  ["b1", "b2", …],
);

散列的每一個鍵指向的不再是單一的標量,而是一個數組。這個數組(不帶標志符@)的名稱是$array_of{key},完整的(帶標志符@的)名稱是@$array_of{key},這樣編寫,Perl就知道這是一個散列的鍵指向的數組。為了使查看代碼時更明確,我們可以加上一組不改變其意義的{},甚至添加空格,如@{ $array_of{key} },這樣更清晰一些。那么就可以像普通數組一樣取用該數組的元素,如${ $array_of{key} }[0]。

代碼1-3中,為了初始化此數組使用以下代碼(第21行):

@{ $value_of_opt{$opt} } = ();

就像初始化普通數組一樣。如果想要在初始化散列時就設置這些數組的元素,那么我們可以這么寫:

%array_of = (
  "keyA" => ["A1", "A2",],
);

此處的中括號表示一個匿名數組的引用,$array_of{"keyA"}實際上是一個指向右側匿名數組的引用。有關引用的詳細知識,會在后續章節進行介紹。

1.4.2 散列的散列

散列的散列:

%hash_of = (
  "keyA" => { "k1" => "valueA1",
              "k2" => "valueA2",
               },
  "keyB" => { "k1" => "valueB1",
              "k2" => "valueB2",
               },
);

$hash_of{"keyA"}指向一個散列,這種用法與數組的散列類似,此時$hash_of{"keyA"}是散列名(不帶標志符%),完整的散列名是%{ $hash_of{"keyA"} },除了名稱有點復雜以外,其余用法與普通散列一樣。要取用此散列的值,使用${ $hash_of{"keyA"} }{"k1"} = "valueA1"。同理,如果你不會混淆,也可以寫為$$hash_of{"keyA"}{"k1"}

散列和數組都可以依照以上規則進行任意深度的嵌套。

主站蜘蛛池模板: 雷州市| 山东省| 崇明县| 泸定县| 揭阳市| 上蔡县| 友谊县| 辽阳市| 兖州市| 长岭县| 伊吾县| 仁化县| 阳高县| 河源市| 合江县| 南涧| 临朐县| 赤壁市| 镇巴县| 磐石市| 江津市| 广河县| 三江| 扎兰屯市| 鹰潭市| 沂源县| 墨脱县| 丘北县| 克拉玛依市| 农安县| 玉田县| 稷山县| 峨眉山市| 北流市| 孟津县| 柳州市| 古丈县| 西峡县| 通江县| 襄城县| 邯郸市|