- 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"}
散列和數組都可以依照以上規則進行任意深度的嵌套。
- Spring Cloud Alibaba微服務架構設計與開發實戰
- 軟件界面交互設計基礎
- 程序員考試案例梳理、真題透解與強化訓練
- TypeScript實戰指南
- Getting Started with React Native
- Android群英傳
- TypeScript 2.x By Example
- Python函數式編程(第2版)
- 創意UI Photoshop玩轉移動UI設計
- Angular Design Patterns
- Get Your Hands Dirty on Clean Architecture
- Java多線程并發體系實戰(微課視頻版)
- 你必須知道的.NET(第2版)
- Python滲透測試編程技術:方法與實踐(第2版)
- Practical Responsive Typography