服務計算--使用 golang 開發 開發 Linux 命令列實用程式 中的 selpg作業3

huyaxiaomuge發表於2021-01-04

本次實驗的內容是使用 golang 開發 selpg




程式設計

輸入指令的格式應該是selpg --s start_page --e end_page [--f | --l lines_per_page] [ --d print_dest] [file_name]

引數要求意義
–s必需起始頁
–e必需結束頁
–f可選根據分頁符翻頁
–l可選根據行數翻頁,每頁的行數
–d可選印表機地址
file_name必需檔案路徑

備註:--f--l是互斥的,如果同時出現將會報錯
根據上述情況,很容易想到利用一個結構體儲存所有內容:

type selpg_parm struct {
	start_page int //開始頁數
	end_page int //結束頁數
	lines_per_page int //每頁行數
	delimited_type string // 'l' for lines-delimited, 'f' for form-feed-delimited default is 'l'
	print_dest string //印表機裝置
	file_name string //輸入途徑,預設為鍵盤輸入
}

selpg的過程分三步走:結構體初始化(輸入)、檢查輸入、操作並輸出。也就是採取以下模式:

func main() {
	inti()
	check(pflag.NArg())
	operation()
}

接下來是具體的實現過程


程式實現

一、結構體初始化(輸入)
首先要做的是將使用者的輸入,根據不同的引數,賦值到結構體中

var parm selpg_parm
var f_flag *bool

//繫結變數
pflag.IntVar(&parm.start_page, "s", -1, "the start Page")
pflag.IntVar(&parm.end_page, "e", -1, "the end Page")
pflag.IntVar(&parm.lines_per_page, "l", -1, "the number of lines per page")
f_flag = pflag.Bool("f", false, "")
pflag.StringVar(&parm.print_dest, "d", "", "")
pflag.Parse()

起始頁和結束頁的初始值都被設定為-1,這會被用於之後判斷輸入是否合法。預設使用line模式,也就是按行數翻頁,每頁的行數初始值為20。但是如果直接賦值20,就會導致允許使用者同時輸入–f和–l的情況發生, 所以暫時設定為-1,後面在判斷輸入是否合法的時候再加以判斷。一個全域性變數f_flag用於判斷使用者是否使用了引數--f。最後使用pflag.Parse()賦值
然後輸入檔名。利用pflag.NArg()得到沒有字首的引數數量。如果是1說明使用者輸入了檔案路徑,直接賦值即可

parm.file_name = ""
if pflag.NArg() == 1 {
	parm.file_name = pflag.Arg(0)
}

二、檢查輸入
我判斷了以下幾種情況

錯誤條件
除檔案路徑外有多餘的引數NArgs > 1
起始頁大於終止頁parm.start_page > parm.end_page
起始頁不是正數parm.start_page < 1
每頁的行數不是正數(在按行數翻頁模式的前提下)parm.lines_per_page < 0
--l--f衝突*f_flag && parm.lines_per_page != -1

關於最後一個錯誤我解釋一下。當*f_flag為真時,說明使用者輸入了--f;當parm.lines_per_page不等於-1時,說明使用者輸入了’–l’,這就可以判斷是否衝突。如果不衝突,且是--l,還需要判斷一下每頁的行數是否是初始值-1。如果是就賦值20(預設值)。這樣就既避免了允許衝突的錯誤,也可以給每頁的行數賦值20
程式碼:

func check(NArgs int) {
	if NArgs < 1 {
		fmt.Println("錯誤:沒有檔案路徑")
		os.Exit(1)
	}
	if NArgs > 1 {
		fmt.Println("錯誤:除檔案路徑外有多餘的引數")
		os.Exit(1)
	}

	if parm.start_page > parm.end_page {
		fmt.Println("錯誤:起始頁大於終止頁")
		os.Exit(1)
	}

	if parm.start_page < 1 {
		fmt.Println("錯誤:起始頁不是正數")
		os.Exit(1)
	}

	if *f_flag && parm.lines_per_page != -1 {
		fmt.Println("錯誤:--l和--f衝突")
		os.Exit(1)
	} else if parm.lines_per_page != -1 {
		if parm.lines_per_page < 0 {
			fmt.Println("錯誤:每頁的行數不是正數")
			os.Exit(1)
		} else {
			parm.delimited_mode = "l"
		}
	} else if parm.lines_per_page == -1 {
		parm.delimited_mode = "l"
		parm.lines_per_page = 20
	} else {
		parm.delimited_mode = "f"
	}
}

三、操作並輸出
首先讀取檔案:

fin := os.Stdin
fout := os.Stdout

var err error
if parm.file_name != "" {
	fin, err = os.Open(parm.file_name)
	if err != nil {
		fmt.Println("錯誤:檔案路徑錯誤")
		os.Exit(1)
	}
	defer fin.Close()
}

然後根據不同的翻頁模式,輸出。在f模式下:

cur_page := 1 //當前頁
if parm.delimited_mode == "f" {
	rd := bufio.NewReader(fin)
	for {
		page, ferr := rd.ReadString('\f')
		if (ferr != nil || ferr == io.EOF) && ferr == io.EOF && cur_page >= parm.start_page && cur_page <= parm.end_page {
			fmt.Fprintf(fout, "%s", page)
			break
		}
		page = strings.Replace(page, "\f", "", -1)
		cur_page++
		if cur_page >= parm.start_page && cur_page <= parm.end_page {
			fmt.Fprintf(fout, "%s", page)
		}
	}
}

l模式下:

cur_line := 0 //當前行
else {
	line := bufio.NewScanner(fin)
	for line.Scan() { //逐行讀入
		if cur_page >= parm.start_page && cur_page <= parm.end_page {
			fout.Write([]byte(line.Text() + "\n"))
		}
		cur_line++
		if cur_line == parm.lines_per_page {
			cur_page++
			cur_line = 0
		}
	}
}


測試結果

首先使用go build selpg.go進行部署。測試檔案test有30行,每一行的內容都是行號
1、預設情況下,輸出第2頁
在這裡插入圖片描述
可以看到第2頁從21行開始,到30行結束
2、每頁的行數為10,輸出第2頁
在這裡插入圖片描述
接下來,嘗試幾種錯誤情況
3、除檔案路徑外有多餘的引數
在這裡插入圖片描述

4、起始頁大於終止頁
在這裡插入圖片描述

5、起始頁不是正數
在這裡插入圖片描述

6、--l--f衝突
在這裡插入圖片描述

7、每頁的行數不是正數
在這裡插入圖片描述


實驗心得

通過這次作業,我對go語言瞭解更深入了,學會了命令列實用程式開發,直觀地感受到了pflag工具的方便

相關文章