selpg本质上就是将一个文件,通过自己设定的分页方式,输出到屏幕或者重定向到其他文件上,或者利用打印机打印出来。根据selpg的c代码,使用go语言实现。
大致实现如下:
引用的包
值得一提的是需要用到 go 封装的 pflag 包,能够方便地解析参数。
import (
"fmt"
"strings"
"errors"
"bufio"
"io"
"bytes"
"os"
"os/exec"
flag "github.com/spf13/pflag"
)
变量初始化
这里进行了五个变量的声明和初始化,各个变量的含义如下解释。接下来的就是一些关于报错输入输出流的绑定、读入数据变量创建等。此外,要使用flag.Parse()解析上面初始化的变量。
var start_page = flag.Int("s", -1, "The start page")
var end_page = flag.Int("e", -1, "The end page")
var line_delim = flag.Bool("l", true, "Delimeter")
var page_len = flag.Int("p", 72, "The legnth of a page")
var print_dest = flag.String("d", "none", "Destination ")
var in_filename string
var err error
const inbuff_size = 16*1024
处理错误信息
发生错误时,终端发出提醒,并结束程序。
func usage(err error) {
if err != nil {
fmt.Printf("Error \n\n", err)
os.Exit(1)
}
}
主函数
根据程序的功能,按流程调用不同的方法即可。具体可参考c语言实现。
func main() {
flag.Parse()
err = process_args()
usage(err)
str, err := process_input()
usage(err)
fmt.Println(str)
fmt.Printf("Finish printing")
}
检查输入参数
对输入的参数,需要检查它们的合法性。比如有无输入、开始页是否小于等于结束页等等,需要考虑到各种情况。
func process_args() error {
if *start_page == -1 || *end_page == -1 {
return errors.New("Page arguments' wrong \n")
}
if *start_page <= 0 || *start_page > (inbuff_size-1) {
return errors.New("Invalid start page \n")
}
if *end_page <= 0 || *end_page > (inbuff_size-1) {
return errors.New("Invalid end page \n")
}
if *start_page > *end_page {
return errors.New("Invalid page range \n")
}
if *line_delim == true && *page_len == 72 {//又是行分割又是也分割
return errors.New("Enter command either -l or -f \n")
}
return nil
}
输入输出
输入的方式有两种,一种是标准输入的模式,一种是文件输入的方式。
对于文件输入的模式来说,需要有一个文件参数,使用os.Open()打开文件,然后就通过file.Read()迭代的读出所有的文件内容。而标准输入没有文件参数。
func process_input() (string, error) {
result := ""
page := 1
line := 0
reader := bufio.NewReader(os.Stdin)
// 如果有文件参数的话
if flag.NArg() > 0 {
in_filename = flag.Arg(0)
file , err := os.Open(in_filename)
if err != nil {
return "", err
}
defer file.Close()
reader = bufio.NewReader(file)
}
if *line_delim {
// 行分割
page = 1
line = 0
for {
str, err := reader.ReadString('\n')
if err == io.EOF {
break
} else if err != nil {
return "", err
}
line++
if line > *page_len {
page++
line = 1
}
if page >= *start_page && page <= *end_page {
result = strings.Join([]string{result, str}, "")
}
}
} else {
// 页分割
page = 1
for {
str, err := reader.ReadString('\f')
if err == io.EOF {
break
} else if err != nil {
return "", err
}
page++
if page >= *start_page && page <= *end_page {
result = strings.Join([]string{result, str}, "")
}
}
}
// 检查起始、结束页是否正确
if page < *start_page {
msg := fmt.Sprintf("Start page is out of the range")
return "", errors.New(msg)
} else if page > *end_page {
msg := fmt.Sprintf("End page is out of the range")
return "", errors.New(msg)
}
//
if *print_dest != "none" {
cmd := exec.Command("lp", "-d" + *print_dest)
cmd.Stdin = strings.NewReader(result)
var stderr bytes.Buffer
cmd.Stderr = &stderr
err = cmd.Run()
if err != nil {
return "", errors.New(fmt.Sprint(err) + ":" + stderr.String())
}
}
return result, nil
}