cowsay
Like CLI apps? Don’t miss the lolcat tutorial as well!
喜欢CLI应用程序吗? 也不要错过lolcat教程!
Cowsay is one of those apps you can’t live without.
Cowsay是您无法没有的那些应用程序之一。
It basically generates ASCII pictures of a cow with any message you pass it to, in the above screenshot using fortune
to generate it. But it’s not limited to the cow domain, it can print penguins, mooses and many other animals.
在上面的屏幕截图中,它使用fortune
来生成它,它基本上会生成包含您传递给它的任何消息的母牛的ASCII图片。 但这不仅限于牛域,它还可以打印企鹅,驼鹿和许多其他动物。
Sounds like a useful app to port to Go!
听起来像是一个有用的应用程序,可移植到Go!
Also, I like the plain english license attached to it:
另外,我喜欢所附的普通英语许可证:
==============
cowsay License
==============
cowsay is distributed under the same licensing terms as Perl: the
Artistic License or the GNU General Public License. If you don't
want to track down these licenses and read them for yourself, use
the parts that I'd prefer:
(0) I wrote it and you didn't.
(1) Give credit where credit is due if you borrow the code for some
other purpose.
(2) If you have any bugfixes or suggestions, please notify me so
that I may incorporate them.
(3) If you try to make money off of cowsay, you suck.
Let’s start by defining the problem. We want to accept input through a pipe, and have our cow say it.
让我们从定义问题开始。 我们想通过管道接受输入,然后让我们的牛说出来。
The first iteration reads the user input from the pipe, and prints it back. Not too much complicated.
第一次迭代从管道读取用户输入,然后将其打印回去。 没有太多复杂。
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
info, _ := os.Stdin.Stat()
if info.Mode()&os.ModeCharDevice != 0 {
fmt.Println("The command is intended to work with pipes.")
fmt.Println("Usage: fortune | gocowsay")
return
}
reader := bufio.NewReader(os.Stdin)
var output []rune
for {
input, _, err := reader.ReadRune()
if err != nil && err == io.EOF {
break
}
output = append(output, input)
}
for j := 0; j < len(output); j++ {
fmt.Printf("%c", output[j])
}
}
We’re missing the cow, and also we need to wrap the message into a balloon, nicely formatted.
我们想念这头牛,还需要将消息包装成一个气球,格式正确。
Here is the first iteration of our program:
这是我们程序的第一次迭代:
package main
import (
"bufio"
"fmt"
"io"
"os"
"strings"
"unicode/utf8"
)
// buildBalloon takes a slice of strings of max width maxwidth
// prepends/appends margins on first and last line, and at start/end of each line
// and returns a string with the contents of the balloon
func buildBalloon(lines []string, maxwidth int) string {
var borders []string
count := len(lines)
var ret []string
borders = []string{"/", "\\", "\\", "/", "|", "<", ">"}
top := " " + strings.Repeat("_", maxwidth+2)
bottom := " " + strings.Repeat("-", maxwidth+2)
ret = append(ret, top)
if count == 1 {
s := fmt.Sprintf("%s %s %s", borders[5], lines[0], borders[6])
ret = append(ret, s)
} else {
s := fmt.Sprintf(`%s %s %s`, borders[0], lines[0], borders[1])
ret = append(ret, s)
i := 1
for ; i < count-1; i++ {
s = fmt.Sprintf(`%s %s %s`, borders[4], lines[i], borders[4])
ret = append(ret, s)
}
s = fmt.Sprintf(`%s %s %s`, borders[2], lines[i], borders[3])
ret = append(ret, s)
}
ret = append(ret, bottom)
return strings.Join(ret, "\n")
}
// tabsToSpaces converts all tabs found in the strings
// found in the `lines` slice to 4 spaces, to prevent misalignments in
// counting the runes
func tabsToSpaces(lines []string) []string {
var ret []string
for _, l := range lines {
l = strings.Replace(l, "\t", " ", -1)
ret = append(ret, l)
}
return ret
}
// calculatemaxwidth given a slice of strings returns the length of the
// string with max length
func calculateMaxWidth(lines []string) int {
w := 0
for _, l := range lines {
len := utf8.RuneCountInString(l)
if len > w {
w = len
}
}
return w
}
// normalizeStringsLength takes a slice of strings and appends
// to each one a number of spaces needed to have them all the same number
// of runes
func normalizeStringsLength(lines []string, maxwidth int) []string {
var ret []string
for _, l := range lines {
s := l + strings.Repeat(" ", maxwidth-utf8.RuneCountInString(l))
ret = append(ret, s)
}
return ret
}
func main() {
info, _ := os.Stdin.Stat()
if info.Mode()&os.ModeCharDevice != 0 {
fmt.Println("The command is intended to work with pipes.")
fmt.Println("Usage: fortune | gocowsay")
return
}
var lines []string
reader := bufio.NewReader(os.Stdin)
for {
line, _, err := reader.ReadLine()
if err != nil && err == io.EOF {
break
}
lines = append(lines, string(line))
}
var cow = ` \ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
`
lines = tabsToSpaces(lines)
maxwidth := calculateMaxWidth(lines)
messages := normalizeStringsLength(lines, maxwidth)
balloon := buildBalloon(messages, maxwidth)
fmt.Println(balloon)
fmt.Println(cow)
fmt.Println()
}
Let’s now make the figure configurable, by adding a stegosaurus
现在,通过添加剑龙使该图可配置
The original application uses the -f
flag to accept a custom figure. So let’s do the same by processing a command line flag.
原始应用程序使用-f
标志来接受自定义图形。 因此,让我们通过处理命令行标志来做同样的事情。
I briefly change the previous program to introduce printFigure()
我简要更改了以前的程序,以引入printFigure()
// printFigure given a figure name prints it.
// Currently accepts `cow` and `stegosaurus`.
func printFigure(name string) {
var cow = ` \ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
`
var stegosaurus = ` \ . .
\ / ` + "`" + `. .' "
\ .---. < > < > .---.
\ | \ \ - ~ ~ - / / |
_____ ..-~ ~-..-~
| | \~~~\\.' ` + "`" + `./~~~/
--------- \__/ \__/
.' O \ / / \ "
(_____, ` + "`" + `._.' | } \/~~~/
` + "`" + `----. / } | / \__/
` + "`" + `-. | / | / ` + "`" + `. ,~~|
~-.__| /_ - ~ ^| /- _ ` + "`" + `..-'
| / | / ~-. ` + "`" + `-. _ _ _
|_____| |_____| ~ - . _ _ _ _ _>
`
switch name {
case "cow":
fmt.Println(cow)
case "stegosaurus":
fmt.Println(stegosaurus)
default:
fmt.Println("Unknown figure")
}
}
and changing main()
to accept a flag and passing it to printFigure()
:
并将main()
更改为接受标志并将其传递给printFigure()
:
func main() {
//...
var figure string
flag.StringVar(&figure, "f", "cow", "the figure name. Valid values are `cow` and `stegosaurus`")
flag.Parse()
//...
printFigure(figure)
fmt.Println()
}
I think we’re at a good point. I just want to make this usable system-wise, without running go run main.go
, so I’ll just type go build
and go install
.
我认为我们处在一个好时机。 我只想使它在系统上可用,而无需运行go run main.go
,所以我只需要输入go build
和go install
。
I can now spend the day with gololcat and gocowsay
我现在可以和gololcat和gocowsay一起度过一天
cowsay