The 2010-08-11 release of the Go programming language includes the dict package to talk to dictionary servers as defined by RFC 2229. Let's check it out.
First, here's a mimimal program that uses the Dial() and Define() functions to display a definition.
package main
import "net/dict"
func main() {
c, _ := dict.Dial("tcp", "dict.org:2628")
defn, _ := c.Define("wn", "go")
for _, result := range defn {
println(string(result.Text))
}
}
This program does no error checking, and has three hardcoded bits: the server (dict.org:2628), the database ("wn") and word to lookup ("go").
For the next version, let's add some flexibility -- lookup items specified on the command line, and let's add some error checking and cleanup:
package main
import (
"net/dict"
"os"
)
func main() {
c, neterr := dict.Dial("tcp", "dict.org:2628")
if neterr == nil {
defer c.Close()
for _, word := range os.Args {
defn, deferr := c.Define("wn", word)
if deferr == nil {
for _, result := range defn {
println(string(result.Text))
}
}
}
}
}
Ok, great, what if you want to switch databases? You can ask the server. If you run the command with no arguments, it will list the available databases using the Dicts() function:
package main
import (
"net/dict"
"os"
)
func main() {
c, neterr := dict.Dial("tcp", "dict.org:2628")
if neterr == nil {
defer c.Close()
if len(os.Args) == 0 {
dicts, dicterr := c.Dicts()
if dicterr == nil {
for _, dl := range dicts {
println(dl.Name, dl.Desc)
}
}
} else {
for _, word := range os.Args {
defn, deferr := c.Define("wn", word)
if deferr == nil {
for _, result := range defn {
println(string(result.Text))
}
}
}
}
}
}
One more update: let's add flags to specify the dict server and database. We'll also update the error processing to show the errors instead of failing silently:
package main
import (
"net/dict"
"flag"
"fmt"
)
var (
db = flag.String("d", "wn", "Dictionary database")
dserver = flag.String("s", "dict.org:2628", "Dictionary Server")
)
func main() {
flag.Parse()
c, neterr := dict.Dial("tcp", *dserver)
if neterr == nil {
defer c.Close()
if len(flag.Args()) == 0 {
dicts, dicterr := c.Dicts()
if dicterr == nil {
for _, dl := range dicts {
fmt.Println(dl.Name, dl.Desc)
}
} else {
fmt.Println(dicterr)
}
} else {
for _, word := range flag.Args() {
defn, dferr := c.Define(*db, word)
if dferr == nil {
for _, result := range defn {
fmt.Println(string(result.Text))
}
} else {
fmt.Println(dferr)
}
}
}
} else {
fmt.Println(neterr)
}
}
There you go--a robust, flexible dictionary client in a little over 40 lines of code.
2 comments:
Your code will be cleaner (and more idiomatic) if you check for errors and then do something instead of putting the whole program inside an if block, ie:
if err != nil {
handleError(err)
}
stuff()
instead of:
if err == nil {
stuff()
}
I know this is a minor issue, but all those closing braces at the end of the program are really ugly, and it uses to be a good idea to follow conventions.
That said, nice example :)
For little programs, I find myself using
func dieOnError(e *os.Error) {
if e != nil {
panic(e.String())
}
}
(Of course, one needs to be careful with os.EOF)
Post a Comment