I'm trying to run a go program using LiteIDE x22 but I get the message
C:/Go/bin/go.exe build [C:/Users/admins/Desktop/desktp/worm_scraper-master]
worm_scraper.go:11:2: cannot find package "github.com/codegangsta/cli" in any of:
C:\Go\src\pkg\github.com\codegangsta\cli (from $GOROOT)
C:\users\admins\gostuff\src\github.com\codegangsta\cli (from $GOPATH)
worm_scraper.go:12:2: cannot find package "github.com/puerkitobio/goquery" in any of:
C:\Go\src\pkg\github.com\puerkitobio\goquery (from $GOROOT)
C:\users\admins\gostuff\src\github.com\puerkitobio\goquery (from $GOPATH)
Error: process exited with code 1.
I think this means it's looking for it on my harddrive instead of online right? (btw I'm pretty clueless about programming just trying to something some else wrote)
how to I get it to access the web?
here's the full code
package main
import (
"errors"
"fmt"
"os"
"os/exec"
"regexp"
"strings"
"github.com/codegangsta/cli"
"github.com/puerkitobio/goquery"
)
const (
MainSite = "https://parahumans.wordpress.com/"
TableOfContents = "https://parahumans.wordpress.com/table-of-contents/"
)
type Arc struct {
Identifier string
Title string
Chapters []Chapter
}
type Chapter struct {
Title string
Url string
Tags []string
Paragraphs []Paragraph
Retries int
DatePosted string
}
type Paragraph string
// Format the paragraph
func (p *Paragraph) Format() {
s := string(*p)
// Handle emphasis
s = strings.Replace(s, "<em>", "*", -1)
s = strings.Replace(s, "</em>", "*", -1)
s = strings.Replace(s, "<i>", "*", -1)
s = strings.Replace(s, "</i>", "*", -1)
// Handle bold
s = strings.Replace(s, "<strong>", "**", -1)
s = strings.Replace(s, "</strong>", "**", -1)
s = strings.Replace(s, "<b>", "**", -1)
s = strings.Replace(s, "</b>", "**", -1)
// Remove new lines
s = strings.Replace(s, "\n", "", -1)
// And random double spaces
s = strings.Replace(s, ". ", ". ", -1)
*p = Paragraph(s)
}
// Return the Arc that the given chapter belongs to
func (ch *Chapter) WhichArc(arcList []*Arc) (*Arc, error) {
for _, arc := range arcList {
if strings.Replace(ch.Title[:2], ".", "", -1) == arc.Identifier {
return arc, nil
}
}
return &Arc{}, errors.New("chapter '" + ch.Title + "' did not match any Arcs")
}
// Parse a chapter and return it
func (ch *Chapter) Parse(done chan bool) {
if ch.Retries > 3 {
panic("Chapter url '" + ch.Url + "' has timed out too many times")
}
// Get the chapter
if strings.HasPrefix(ch.Url, "http") == false {
// Make sure it begins with http so goquery can use it
ch.Url = "https://" + ch.Url
}
doc, err := goquery.NewDocument(ch.Url)
if err != nil {
// Try again
ch.Retries++
go ch.Parse(done)
return
}
// Set the new chapter title
ch.Title = doc.Find("h1.entry-title").Text()
// Set the tags
doc.Find(".entry-meta a[rel=tag]").Each(func(_ int, s *goquery.Selection) {
ch.Tags = append(ch.Tags, s.Text())
if len(ch.Tags) == 0 {
ch.Tags = append(ch.Tags, "NONE")
}
})
// Get the date it was posted
ch.DatePosted = doc.Find("time.entry-date").Text()
// Now we'll get all the paragraphs
doc.Find(".entry-content > p").Each(func(_ int, s *goquery.Selection) {
// Check for the previous/next links
if len(s.Find("a").Nodes) > 0 {
return
}
// Get the paragraph HTML
st, _ := s.Html()
para := Paragraph("")
// Get the actual paragraph
if val, exists := s.Attr("padding-left"); exists && val == "30px" {
// Check to see if the paragraph is special (indented) block
para = Paragraph(" " + st)
} else if val, exists := s.Attr("text-align"); exists && val == "center" {
// Otherwise check to see if it's a separator paragraph
para = Paragraph("----------")
} else {
// It's just a normal paragraph in this case
para = Paragraph(st)
}
// And add the paragraph to the chapter
para.Format()
ch.Paragraphs = append(ch.Paragraphs, para)
})
// Finally, let's signal a success
done <- true
}
// Return a slice of Arcs extracted from the table of contents
func ParseArcs(s string) []*Arc {
arcs := []*Arc{}
r, _ := regexp.Compile(`[0-9]+`)
for _, line := range strings.Split(s, "\n") {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "Arc") {
arcs = append(arcs, &Arc{
Identifier: r.FindString(line),
Title: line,
})
} else if strings.HasPrefix(line, "Epilogue") {
arcs = append(arcs, &Arc{
Identifier: "E",
Title: line,
})
}
}
return arcs
}
func main() {
// Define the app
app := cli.NewApp()
app.Name = "Worm Scraper"
app.Usage = "A tool to let you get an updated EPUB copy of the serial web novel Worm, by Wildbow"
app.Version = "1.0"
app.Author = "Benjamin Harris"
// Define the application flags
app.Flags = []cli.Flag{
cli.BoolFlag{"pdf", "Save the book as a PDF instead of an EPUB, if possible"},
cli.BoolFlag{"with-link", "Include a link to the chapter online"},
cli.BoolFlag{"with-tags", "Include the tags each chapter was posted under"},
cli.BoolFlag{"with-date", "Include the date each chapter was posted"},
}
// The heart of the application
app.Action = func(context *cli.Context) {
// Starting the program
fmt.Println("Starting to scrape Worm")
// Get the list of arcs from the table of contents
fmt.Println("Gathering links from table of contents...")
contents, err := goquery.NewDocument(TableOfContents)
if err != nil {
panic("Failed to get the table of contents! " + err.Error())
}
// Parse the arcs
arcs := ParseArcs(contents.Find(".entry-content").Text())
// Now get the links for the arc chapters
contents.Find(".entry-content a:not([class*=share-icon])").Each(func(_ int, s *goquery.Selection) {
ch := Chapter{}
ch.Title = strings.Replace(strings.TrimSpace(s.Text()), "\n", "", -1)
ch.Url, _ = s.Attr("href")
if ch.Title == "" {
return
}
arc, _ := ch.WhichArc(arcs)
arc.Chapters = append(arc.Chapters, ch)
})
// Manually add missing chapter in Epilogue
c := Chapter{
Title: "E.2",
Url: "https://parahumans.wordpress.com/2013/11/05/teneral-e-2/",
}
a, _ := c.WhichArc(arcs)
a.Chapters = append(a.Chapters, c)
copy(a.Chapters[1+1:], a.Chapters[1:])
a.Chapters[1] = c
// Now start getting the chapters
chapters := 0
done := make(chan bool)
for _, arc := range arcs {
for i, _ := range arc.Chapters {
chapters++
go arc.Chapters[i].Parse(done)
}
}
fmt.Println("Starting to parse", chapters, "chapters")
fmt.Print("Finished: ")
totalChapters := chapters
for {
select {
case <-done:
chapters--
fmt.Print(totalChapters-chapters, ",")
}
if chapters == 0 {
// We're done with all the chapters
close(done)
fmt.Println()
break
}
}
// And let's write all this stuff to a file now
fmt.Println("Saving results to file...")
f, err := os.OpenFile("Worm.md", os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
if err != nil {
panic(err)
}
defer f.Close()
// Define pagebreak
PageBreak := "\n\n"
// Write the cover
f.WriteString("# Worm\n\n")
f.WriteString("By Wildbow\n\n")
f.WriteString("Website: " + MainSite)
// Now loop through the Arcs
for _, arc := range arcs {
f.WriteString(PageBreak + "# " + arc.Title)
for _, chapter := range arc.Chapters {
f.WriteString("\n\n")
f.WriteString("## " + chapter.Title + "\n\n")
if context.Bool("with-tags") {
f.WriteString("**Tags:** " + strings.Join(chapter.Tags, ", ") + " ")
}
if context.Bool("with-date") {
f.WriteString("**Date:** " + chapter.DatePosted + " ")
}
if context.Bool("with-link") {
f.WriteString("**Link:** " + chapter.Url + " ")
}
f.WriteString("\n\n")
// Now save the chapter's paragraphs
for _, p := range chapter.Paragraphs {
f.WriteString(string(p) + "\n\n")
}
}
}
// Now let's try to convert the markdown file into an ebook format (epub, pdf)
fmt.Print("Attempting to convert Markdown file... ")
cmdText := []string{"-S", "Worm.md", "--epub-chapter-level", "2", "-o", "Worm.epub"}
if context.Bool("pdf") {
cmdText = []string{"Worm.md", "-o", "Worm.pdf"}
PageBreak = `<div style="page-break-after: always;"></div>`
}
cmd := exec.Command("pandoc", cmdText...)
err = cmd.Run()
if err != nil {
fmt.Println("Conversion failed! Make sure you've installed Pandoc (http://johnmacfarlane.net/pandoc/installing.html) if you want to convert the generated Markdown file to an ebook compatible format. In the meantime, we've left you the Markdown file.")
} else {
_ = os.Remove("Worm.md")
fmt.Println("Completed!")
}
}
// Run the application
app.Run(os.Args)
}
oh also would it be possible to modify it to output as .txt or .mobi? if not I'll just convert using Calibre. Thanks in advance.
Oh if it matters I'm using windows 7 64-bit
The go compiler doesn't import the libraries directly from the internet but it does know how to fetch them for you. When you import something like github.com/codegangsta/cli it doesn't look for it on that URL but instead it looks for it on your GOPATH/src folder.
The go get command can fetch the library for you in it's URL and download it to your GOPATH.
If you have already setup your GOPATH (if not, read How to Write Go Code) then before running your code run the command go get library for the go tool to download it for you. In your example you should run the following commands:
go get github.com/codegangsta/cli
go get github.com/puerkitobio/goquery
That would download the libraries to GOPATH/src/github.com/codegangsta/cli and GOPATH/src/github.com/puerkitobio/goquery respectively.
Related
I think I need a better solution than my case switch as the struct gains more fields my function will become verbose. Is there a way to swap my switch for a loop?
I have the following code
type Things struct {
StreetNames []string `json:"streetNames"`
Letters []string `json:"letters"`
MaleNames []string `json:"maleNames"`
}
func CheckCategories(data *Things, filePath string) error {
errMsg := "list has no values or is a missing category in File: " + filePath
categories := []string{"street_name", "letter", "male_first_name"}
for _, value := range categories {
switch value {
case "street_name":
if len(data.StreetNames) == 0 {
return errors.New("street_name " + errMsg)
}
case "letter":
if len(data.Letters) == 0 {
return errors.New("letter " + errMsg)
}
case "male_first_name":
if len(data.MaleNames) == 0 {
return errors.New("male_first_name " + errMsg)
}
}
}
return nil
}
This works for me but the real struct contains 12 fields which makes my functions long and repetitive.
I tried
for _, value := range categories {
if len("data." + value) == 0 {
return errors.New(value + errMsg)
}
But when I ran the code I took a moment to notice it wasn't working as intended, Im getting the length of the string. I have tried data[value] but that didn't work either.
Is there a way to swap my switch for a loop?
You could do the following:
type Things struct {
StreetNames []string `json:"streetNames"`
Letters []string `json:"letters"`
MaleNames []string `json:"maleNames"`
}
func CheckCategories(data *Things, filePath string) error {
errMsg := "list has no values or is a missing category in File: " + filePath
categories := []struct{
name string
slice []string
}{
{"street_name", data.StreetNames},
{"letter", data.Letters},
{"male_first_name", data.MaleNames},
}
for _, v := range categories {
if len(v.slice) == 0 {
return errors.New(v.name + " " + errMsg)
}
}
return nil
}
How can I add an extra column to a container.NewGridWithColumns in Go/Fyne?
I have a number of items (containers) rendered in a ColumnGrid. Then via a dialog I want to add an item. The problem is that I can not find a way to extend the original ColumnGrid.
My code:
func main() {
a := app.New()
w = a.NewWindow("myApp")
window = container.NewBorder(toolbar(), footer(), nil, nil, content())
w.SetContent(window)
w.ShowAndRun()
}
func content() *fyne.Container {
top := topRow()
bottom := bottomRow()
return container.NewGridWithRows(2, top, bottom)
}
var items []*fyne.Container
func bottomRow() *fyne.Container {
items = nil
db := sql.NewDB()
list, err := db.List()
if err != nil {
//handle error
}
for _, l := range list {
items = append(items, renderChart(l))
}
ct = container.NewGridWithColumns(len(items))
for _, item := range items {
ct.Add(item)
}
return ct
}
func dlgAdd() {
entry := widget.NewEntry()
entry.PlaceHolder = "name"
e := container.NewGridWithRows(2, entry)
d := dialog.NewCustomConfirm(
"Add Item",
"Add",
"Cancel",
e,
func(v bool) {
if !v {
//Cancelled
return
}
if entry.Text == "" {
//without input
return
}
//write entry.Text to db
db := sql.NewDB()
err := db.AddItem(entry.Text)
if err != nil {
return
}
//report succes
i := dialog.NewInformation("Succes", fmt.Sprintf("Item %s added", entry.Text), w)
i.Show()
i.SetOnClosed(func() {
Here is the problem, how to add an extra column to the container ct
and then add the item from the dialog to the new column
This will not work
ct = container.NewGridWithColumns(len(ct.Objects) + 1)
items = append(items, renderChart(entry.Text))
for _, item := range items {
ct.Add(item)
}
This will also not work
bottomRow()
w.Canvas().Refresh(window)
})
}, w)
d.Show()
I am really stuck here.
I found the solution :
ct.Add(newObject)
ct.Layout = (container.NewGridWithColumns(len(ct.Objects))).Layout
ct.Refresh()
Yes, Container.Add is the solution, you just needed to save a reference to the Container instead of creating a new one.
Depending on the Layout it may not always be necessary to change it, for example GridWithRows(...) will keep adding columns if the number of items is increased (because the row count is fixed).
I'd like to write a method that will populate a Go Language array with the common timezones that are accepted by the time.Format() call, for use in an HTML template (Form select to allow them to read and choose their timezone). Is there a common way to do this?
To get a list of time zones, you can use something like:
package main
import (
"fmt"
"io/ioutil"
"strings"
)
var zoneDirs = []string{
// Update path according to your OS
"/usr/share/zoneinfo/",
"/usr/share/lib/zoneinfo/",
"/usr/lib/locale/TZ/",
}
var zoneDir string
func main() {
for _, zoneDir = range zoneDirs {
ReadFile("")
}
}
func ReadFile(path string) {
files, _ := ioutil.ReadDir(zoneDir + path)
for _, f := range files {
if f.Name() != strings.ToUpper(f.Name()[:1]) + f.Name()[1:] {
continue
}
if f.IsDir() {
ReadFile(path + "/" + f.Name())
} else {
fmt.Println((path + "/" + f.Name())[1:])
}
}
}
output:
Africa/Abidjan
Africa/Accra
Africa/Addis_Ababa
Africa/Algiers
Africa/Asmara
Africa/Asmera
Africa/Bamako
Africa/Bangui
...
Go's time pkg uses a timezone database.
You can load a timezone location like this:
loc, err := time.LoadLocation("America/Chicago")
if err != nil {
// handle error
}
t := time.Now().In(loc)
The Format function is not related to setting the time zone, this function takes a fixed reference time that allows you to format the date how you would like. Take a look at the time pkg docs.
For instance:
fmt.Println(t.Format("MST")) // outputs CST
Here is a running example
Here is an example: https://play.golang.org/p/KFGQiW5A1P-
package main
import (
"fmt"
"io/ioutil"
"strings"
"unicode"
)
func main() {
fmt.Println(GetOsTimeZones())
}
func GetOsTimeZones() []string {
var zones []string
var zoneDirs = []string{
// Update path according to your OS
"/usr/share/zoneinfo/",
"/usr/share/lib/zoneinfo/",
"/usr/lib/locale/TZ/",
}
for _, zd := range zoneDirs {
zones = walkTzDir(zd, zones)
for idx, zone := range zones {
zones[idx] = strings.ReplaceAll(zone, zd+"/", "")
}
}
return zones
}
func walkTzDir(path string, zones []string) []string {
fileInfos, err := ioutil.ReadDir(path)
if err != nil {
return zones
}
isAlpha := func(s string) bool {
for _, r := range s {
if !unicode.IsLetter(r) {
return false
}
}
return true
}
for _, info := range fileInfos {
if info.Name() != strings.ToUpper(info.Name()[:1])+info.Name()[1:] {
continue
}
if !isAlpha(info.Name()[:1]) {
continue
}
newPath := path + "/" + info.Name()
if info.IsDir() {
zones = walkTzDir(newPath, zones)
} else {
zones = append(zones, newPath)
}
}
return zones
}
I tried to use i18n but it can't work
i18n is below.
http://beego.me/docs/module/i18n.md
routers/init.go:
// Initialized language type list.
langs := strings.Split(models.Cfg.MustValue("lang", "types"), "|")
names := strings.Split(models.Cfg.MustValue("lang", "names"), "|")
langTypes = make([]*langType, 0, len(langs))
for i, v := range langs {
langTypes = append(langTypes, &langType{
Lang: v,
Name: names[i],
})
}
for _, lang := range langs {
beego.Trace("Loading language: " + lang)
if err := i18n.SetMessage(lang, "conf/"+"locale_"+lang+".ini"); err != nil {
beego.Error("Fail to set message file: " + err.Error())
return
}
}
routers/router.go
// Initialized language type list.
langs := strings.Split(models.Cfg.MustValue("lang", "types"), "|")
names := strings.Split(models.Cfg.MustValue("lang", "names"), "|")
langTypes = make([]*langType, 0, len(langs))
for i, v := range langs {
langTypes = append(langTypes, &langType{
Lang: v,
Name: names[i],
})
}
for _, lang := range langs {
beego.Trace("Loading language: " + lang)
if err := i18n.SetMessage(lang, "conf/"+"locale_"+lang+".ini"); err != nil {
beego.Error("Fail to set message file: " + err.Error())
return
}
}
I just copied and paste.
It is my full code.
https://github.com/shinriyo/sample_i18n
I'd like to know minimum code.
it is error
bee run
2015/05/24 17:48:56 [INFO] Uses 'sample_i18n' as 'appname'
2015/05/24 17:48:56 [INFO] Initializing watcher...
2015/05/24 17:48:56 [TRAC] Directory(/Users/shinriyo/src/sample_i18n/controllers)
2015/05/24 17:48:56 [TRAC] Directory(/Users/shinriyo/src/sample_i18n)
2015/05/24 17:48:56 [TRAC] Directory(/Users/shinriyo/src/sample_i18n/routers)
2015/05/24 17:48:56 [TRAC] Directory(/Users/shinriyo/src/sample_i18n/tests)
2015/05/24 17:48:56 [INFO] Start building...
main.go:4:2:
routers/init.go:2:1: expected 'package', found 'IDENT' langs
2015/05/24 17:48:56 [ERRO] ============== Build failed ===================
The best way I found was that:
Create translation files inside conf/ folder
Configure your translation list at conf/app.conf. My languages are en-US and pt-BR
lang_types = "en-US|pt-BR"
Create file baseController.go inside controller/baseController/ folder
with this code
package controllers
import (
"github.com/astaxie/beego"
"github.com/beego/i18n"
"strings"
)
var langTypes []string // Languages that are supported.
// baseController represents base router for all other app routers.
// It implemented some methods for the same implementation;
// thus, it will be embedded into other routers.
type BaseController struct {
beego.Controller // Embed struct that has stub implementation of the interface.
i18n.Locale // For i18n usage when process data and render template.
}
func init() {
beego.AddFuncMap("i18n", i18n.Tr)
// Initialize language type list.
langTypes = strings.Split(beego.AppConfig.String("lang_types"), "|")
// Load locale files according to language types.
for _, lang := range langTypes {
beego.Trace("Loading language: " + lang)
if err := i18n.SetMessage(lang, "conf/"+"locale_"+lang+".ini"); err != nil {
beego.Error("Fail to set message file:", err)
return
}
}
}
// Prepare implemented Prepare() method for baseController.
// It's used for language option check and setting.
func (this *BaseController) Prepare() {
// Reset language option.
this.Lang = "" // This field is from i18n.Locale.
beego.Trace("running prepare")
// 1. Get language information from 'Accept-Language'.
al := this.Ctx.Request.Header.Get("Accept-Language")
if len(al) > 4 {
al = al[:5] // Only compare first 5 letters.
if i18n.IsExist(al) {
this.Lang = al
}
}
beego.Trace("Accept-Language is " + al)
// 2. Default language is English.
if len(this.Lang) == 0 {
this.Lang = "en-US"
}
// Set template level language option.
this.Data["Lang"] = this.Lang
}
Use your custom BaseController in other controller.
package controllers
import (
bc "github.com/myFirstApp/controllers/baseController"
)
type LoginController struct {
bc.BaseController
}
func (c *LoginController) Get() {
c.TplNames = "login.tpl"
}
Use i18n in templates
{{i18n .Lang "login.capitalized"}}
My locale_en-US.ini
login = login
[login]
uppercase = LOGIN
capitalized = Login
package controllers
import (
"github.com/astaxie/beego"
"github.com/beego/i18n"
"os"
"strings"
)
type BaseController struct {
beego.Controller
}
func (b *BaseController) settingLocales() {
// load locales with locale_LANG.ini files
langs := "en-US|zh-CN"
for _, lang := range strings.Split(langs, "|") {
lang = strings.TrimSpace(lang)
files := []string{"conf/" + "locale_" + lang + ".ini"}
if fh, err := os.Open(files[0]); err == nil {
fh.Close()
} else {
files = nil
}
if err := i18n.SetMessage(lang, "conf/"+"locale_"+lang+".ini", files...); err != nil {
beego.Error("Fail to set message file: " + err.Error())
os.Exit(2)
}
}
Las := i18n.ListLangs()
beego.Debug("langs:", Las)
}
func (b *BaseController) Get() {
b.settingLocales()
if i18n.IsExist("en-US") {
b.Data["json"] = i18n.Tr("en-US", "hi")
} else {
b.Data["json"] = s
}
b.ServeJson()
}
create file locale_en-US.ini and locale_zh-CN.ini in conf/
in locale_en-US.ini like:
hi = hello
bye = goodbye
I'm parsing through the triples of the Freebase RDF compressed and streaming with the XML package in Golang. However, I'm getting an out of memory error.
Do I have to garbage-collect? How can I do that? How can I clear the memory after I'm doing writing that triple to the XML file?
Here's my code: http://play.golang.org/p/dWvbtcs7wy
package main
import(
"bufio"
"flag"
"fmt"
"io"
"net/url"
"os"
"regexp"
"strings"
)
var inputFile = flag.String("infile", "freebase-rdf", "Input file path")
var filter, _ = regexp.Compile("^file:.*|^talk:.*|^special:.*|^wikipedia:.*|^wiktionary:.*|^user:.*|^user_talk:.*")
type Redirect struct {
Title string `xml:"title,attr"`
}
type Page struct {
Title string `xml:"title"`
Abstract string `xml:""`
}
func CanonicaliseTitle(title string) string{
can := strings.ToLower(title)
can = strings.Replace(can, " ", "_", -1)
can = url.QueryEscape(can)
return can
}
func convertFreebaseId(uri string) string{
if strings.HasPrefix(uri, "<") && strings.HasSuffix(uri, ">") {
var id = uri[1 : len(uri)-1]
id = strings.Replace(id, "http://rdf.freebase.com/ns", "", -1)
id = strings.Replace(id, ".", "/", -1)
return id
}
return uri
}
func parseTriple(line string) (string, string, string){
var parts = strings.Split(line, "\t")
subject := convertFreebaseId(parts[0])
predicate := convertFreebaseId(parts[1])
object := convertFreebaseId(parts[2])
return subject, predicate, object
}
var (
validRegexp = regexp.MustCompile("^[A-Za-z0-9][A-Za-z0-9_-]*$")
englishRegexp = regexp.MustCompile("#en")
)
func validTitle(content []string) bool{
for _, v := range content{
if !englishRegexp.MatchString(v) && len(v) > 1 && strings.Index(v, "[]") != -1{
}
}
return true
}
func validText(content []string) bool{
for _, v := range content{
if !validRegexp.MatchString(v) && len(v) > 1 && strings.Index(v, "[]") != -1{
return false
}
}
return true
}
func processTopic(id string, properties map[string][]string, file io.Writer){
if validTitle(properties["/type/object/name"]) && validText(properties["/common/document/text"]){
fmt.Fprintf(file, "<card>\n")
fmt.Fprintf(file, "<title>\"%s\"</title>\n", properties["/type/object/name"])
fmt.Fprintf(file, "<image>\"%s/%s\"</image>\n", "https://usercontent.googleapis.com/freebase/v1/image", id)
fmt.Fprintf(file, "<text>\"%s\"</text>\n", properties["/common/document/text"])
fmt.Fprintf(file, "<facts>\n")
for k, v := range properties{
for _, value := range v{
fmt.Fprintf(file, "<fact property=\"%s\">%s</fact>\n", k, value)
}
}
fmt.Fprintf(file, "</facts>\n")
fmt.Fprintf(file, "</card>\n")
}
}
func main(){
var current_mid = ""
current_topic := make(map[string][]string)
f, err := os.Open(*inputFile)
if err != nil {
fmt.Println(err)
return
}
r := bufio.NewReader(f)
xmlFile, _ := os.Create("freebase.xml")
line, err := r.ReadString('\n')
for err == nil{
subject, predicate, object := parseTriple(line)
if subject == current_mid{
current_topic[predicate] = append(current_topic[predicate], object)
}else if len(current_mid) > 0{
processTopic(current_mid, current_topic, xmlFile)
current_topic = make(map[string][]string)
}
current_mid = subject
line, err = r.ReadString('\n')
}
processTopic(current_mid, current_topic, xmlFile)
if err != io.EOF {
fmt.Println(err)
return
}
}
I'm not sure that this is your problem, although reading your code it seems you're not leaking anything - but you can tune GC behavior a bit with SetGCPercent() http://golang.org/pkg/runtime/debug/#SetGCPercent
According to TFM, a collection is triggered when the ratio of freshly allocated data to live data remaining after the previous collection reaches this percentage.. The default rate is 100%, meaning for programs that make lots of small allocations and hold lots of RAM, the overhead can be huge. I had an HTTP cache take up over 200% the cache size once. Try tuning the percentage to somewhere around 10% and see if it helps.