How do I save an entire string as a txt file in Go? - string

I'm creating a simple word processing program in Go. From the command line, I have two prompts:
$Enter Title:
$Enter Body:
The program is supposed to save the document as a txt file and print it to the command line. The program works if the user user types in a one-word Title and a one-word Body. But if the user types in a several-word title, this will happen:
$Enter Title: Here is a title
$Enter Body: s
$ title
-bash: title: command not found
Here is the code I have so far:
package main
import (
"fmt"
"io/ioutil"
)
//Create struct for a document
type Document struct {
Title string
Body []byte
}
//Save document as txt file
func (p *Document) save() error {
filename := p.Title + ".txt"
return ioutil.WriteFile(filename, p.Body, 0600)
}
//Load document
func loadPage(title string) (*Document, error) {
filename := title + ".txt"
body, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
return &Document{Title: title, Body: body}, nil
}
//Input document title and body.
func main() {
fmt.Print("Enter Title: ")
var Title string
fmt.Scanln(&Title)
fmt.Print("Enter Body: ")
var Body []byte
fmt.Scanln(&Body)
//Save document and display on command line
p1 := &Document{Title: Title, Body: []byte(Body)}
p1.save()
p2, _ := loadPage(Title)
fmt.Println(string(p2.Body))
}

What about using bufio.ReadString instead of fmt.Scanln?
Not 100% how Scanln works, but I am pretty sure the issue comes from a misuse of that function.
Example with bufio:
package main
import (
"bufio"
"fmt"
"io/ioutil"
"log"
"os"
"strings"
)
// Document represent the document's data.
type Document struct {
Title string
Body []byte
}
// Save dumps document as txt file on disc.
func (p *Document) save() error {
filename := p.Title + ".txt"
return ioutil.WriteFile(filename, p.Body, 0600)
}
// loadPage loads a document from disc.
func loadPage(title string) (*Document, error) {
filename := title + ".txt"
body, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
return &Document{Title: title, Body: body}, nil
}
// Input document title and body.
func main() {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter Title: ")
title, err := reader.ReadString('\n')
if err != nil {
log.Fatal(err)
}
title = strings.TrimSpace(title)
fmt.Print("Enter Body: ")
body, err := reader.ReadString('\n')
if err != nil {
log.Fatal(err)
}
body = strings.TrimSpace(body)
//Save document and display on command line
p1 := &Document{Title: title, Body: []byte(body)}
if err := p1.save(); err != nil {
log.Fatal(err)
}
p2, err := loadPage(title)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(p2.Body))
}

Related

uint8 to literal string representation

I'm trying to access the HackerNews API endpoint with a given ID 22024283 which represents a particular news item e.g https://hacker-news.firebaseio.com/v0/22024283.json
This itemID is of type uint8and I need to convert this to it's string representation to insert into the URL.
I cannot use strconv.Itoa(int(id)) as that will produce the number 91 and not preserve 22024283.
Any help on this would be appreciated. Here is my code so far, function of interest is GetHackerNewsItem():
import (
"fmt"
"io/ioutil"
"net/http"
"strconv"
"time"
)
//Client represents connection to firebase datastore
type Client struct {
BASEURI string
Version string
Suffix string
}
type Item struct {
id int `json:"id"`
itemtype string `json:"itemtype"`
by string `json:"by"`
time time.Time `json:"time"`
kids []int `json:"kids"`
url string `json:"url"`
score int `json:"score"`
text string `json:"text"`
title string `json:"title"`
descendants int `json:"descendants"`
}
//Connect to firebase datastore
func NewHackerNewsClient() *Client {
var client Client
client.BASEURI = "https://hacker-news.firebaseio.com/"
client.Version = "v0"
client.Suffix = ".json"
return &client
}
func MakeHTTPRequest(url string) ([]byte, error) {
response, err := http.Get(url)
if err != nil {
fmt.Printf("The http request failed with the error %s\n", err)
}
body, err := ioutil.ReadAll(response.Body)
if err != nil {
fmt.Printf("Failed to read response data with the error %s\n", err)
return nil, err
}
return body, nil
}
func (client *Client) GetHackerNewsItem(id uint8) []byte {
itemID := strconv.Itoa(int(id))
fmt.Printf(itemID)
url := client.BASEURI + client.Version + itemID + client.Suffix
fmt.Printf(url)
item, _ := MakeHTTPRequest(url)
fmt.Print(item)
return item
}
func (client *Client) GetTopStories() {
url := client.BASEURI + client.Version + "/topstories/" + client.Suffix
itemArray, _ := MakeHTTPRequest(url)
for i := 0; i < len(itemArray); i++ {
item := client.GetHackerNewsItem(itemArray[i])
fmt.Print(item)
}
}
func main() {
client := NewHackerNewsClient()
client.GetTopStories()
}
itemArray, _ := MakeHTTPRequest(url)
itemArray must be unmarshaled like
dat := make([]uint64, 0)
if err := json.Unmarshal(itemArray, &dat); err != nil {
panic(err)
}

how to return struct duitonary in golang funtion

i need to retrun struct duitonary from a funtion and when it run script i'm gettting cannot use res (type []exceldata) as type []struct {} in return argument
I have created struct in my go script and i added values to that and added to array now i need to return it to main funtion
package main
import (
"fmt"
"database/sql"
_ "github.com/go-sql-driver/mysql"
"github.com/360EntSecGroup-Skylar/excelize"
"log"
)
type exceldata struct {
username string
rfid string
user string
}
func read() []struct{} {
exdata := exceldata{}
res := []exceldata{}
f, err := excelize.OpenFile("./required_details.xlsx")
if err != nil {
fmt.Println(err)
return res
}
// Get value from cell by given worksheet name and axis.
/*cell, err := f.GetCellValue("Sheet1", "A566")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(cell)*/
// Get all the rows in the Sheet1.
rows, err := f.GetRows("Sheet1")
for _, row := range rows {
if row[0] != "eof"{
exdata.username = row[0]
exdata.rfid = row[1]
exdata.user = row[2]
res = append(res, exdata)
fmt.Println(res)
}else{
return res
}
}
return res;
}
func main() {
fmt.Println("Go MySQL Tutorial")
resexceldata := []exceldata{}
resexceldata =read()
fmt.Println("Routes are Loded.")
}
You are already difining exceldata as a type, so you should use that type:
type exceldata struct
...
func read() []exceldata {
...
}

I can't return string slice like I would like to. Passing just the last one

I wrote this code to get the list of the file in directory, appending the names in a slice and one by one open them, after I open a file I search for some words in the file and if found write them in a new file.
But I always get the same words in the new files and I can't figure out why
package main
import (
"bufio"
"fmt"
"io/ioutil"
"log"
"os"
"strings"
"time"
)
const dir_to_read_path string = "path"
func main() {
start := time.Now()
temp_string_filename := ""
temp_string_filename_counter := 0
//defer list_file()
// just pass the file name
for k := range list_file() {
temp_string_filename = list_file()[temp_string_filename_counter]
if true {
k = k
}
temp_string_filename_counter++
b, err := ioutil.ReadFile(temp_string_filename)
if err != nil {
fmt.Print(err)
}
// convert content to a 'string'
str := string(b)
control_params := []string{"numpy", "grabscreen", "cv2", "time", "os", "pandas", "tqdm", "collections", "models", "random", "inception_v3", "googlenet", "shuffle", "getkeys", "tflearn", "directkeys", "statistics", "motion", "tflearn.layers.conv", "conv_2d", "max_pool_2d", "avg_pool_2d", "conv_3d", "max_pool_3d", "avg_pool_3d"}
temp_string_filename = dir_to_read_path + "output_" + temp_string_filename
fmt.Println("Writing file n. ", k)
file, err := os.Create(temp_string_filename)
if err != nil {
log.Fatal("Cannot create file", err)
}
for _, z := range isValueInList(control_params, str, list_file()) {
fmt.Fprintf(file, z)
fmt.Fprintf(file, "\n")
}
defer file.Close()
elapsed := time.Since(start)
log.Printf("Execution took %s", elapsed)
}
}
func isValueInList(list []string, file_string string, read_file []string) []string {
encountered_modules := make([]string, 0, 10)
temp_string_filename := ""
temp_string_filename_counter := 0
encountered := map[string]bool{}
result := make([]string, 0, 10)
final_result := [][]string{}
for z := range read_file {
fmt.Println("Reading file n. ", z)
temp_string_filename = read_file[temp_string_filename_counter]
f, _ := os.Open(temp_string_filename)
defer f.Close()
scanner := bufio.NewScanner(f)
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
line := scanner.Text()
for _, v := range list {
if v == line {
encountered_modules = append(encountered_modules, line)
}
}
}
for v := range encountered_modules {
if encountered[encountered_modules[v]] == true {
// Do not add duplicate.
} else {
// Record this element as an encountered element.
encountered[encountered_modules[v]] = true
result = append(result, encountered_modules[v])
}
}
temp_string_filename_counter++
final_result = append(final_result, result)
}
return result
}
func list_file() []string {
files_names := make([]string, 0, 10)
files, err := ioutil.ReadDir("./")
if err != nil {
log.Fatal(err)
}
for _, f := range files {
if strings.HasSuffix(f.Name(), ".txt") {
files_names = append(files_names, string(f.Name()))
}
}
return files_names
}
It's hard to be sure, since your code is difficult to read, but this looks particularly suspicious (in pseudocode),
// main
for each file in list_file() {
result = {
// isValueInList
var result
for each file in list_file() {
for each word in file {
if word in wordlist and not in result {
result = append(result, word)
}
}
}
// all the words in wordlist in any of the files
return result
}
// main
write result
}
There are other problems with your code.
Here's a more readable example (a first draft), of what you appear to be trying to do (Python modules in Python files?):
package main
import (
"bufio"
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
)
var modules = map[string]bool{
"numpy": true, "grabscreen": true, "cv2": true, "time": true, "os": true, "pandas": true, "tqdm": true, "collections": true,
"models": true, "random": true, "inception_v3": true, "googlenet": true, "shuffle": true, "getkeys": true, "tflearn": true,
"directkeys": true, "statistics": true, "motion": true, "tflearn.layers.conv": true, "conv_2d": true,
"max_pool_2d": true, "avg_pool_2d": true, "conv_3d": true, "max_pool_3d": true, "avg_pool_3d": true,
}
func findWords(filename string, lexicon map[string]bool) error {
f, err := os.Open(filename)
if err != nil {
return err
}
defer f.Close()
words := make(map[string]bool)
s := bufio.NewScanner(f)
s.Split(bufio.ScanWords)
for s.Scan() {
word := s.Text()
if _, exists := lexicon[word]; exists {
words[word] = true
}
}
if s.Err(); err != nil {
return err
}
var buf bytes.Buffer
for word := range words {
buf.WriteString(word)
buf.WriteString("\n")
}
if buf.Len() > 0 {
err := ioutil.WriteFile(filename+`.words`, buf.Bytes(), 0666)
if err != nil {
return err
}
}
return nil
}
func main() {
dir := `./`
files, err := ioutil.ReadDir(dir)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
for _, file := range files {
filename := file.Name()
if filepath.Ext(filename) != ".py" {
continue
}
findWords(filename, modules)
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
}
}
There are a few mistakes in your code, so i've rewritten most of the code.
What i did :
1) open a file
2) read a line
3) compare it
4) check if the target file exists
5) if not, create it
6) if it does, append to it
7) write to it
8) close target file
9) goto 2 if there are more lines
10) goto 1 if there are more files
I tried to make it as much as readable for everybody so that everybody can understand it.
package main
import (
"bufio"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"strconv"
"strings"
"time"
)
const readDir string = "./"
var startTime time.Time
func main() {
for noFile, fileName := range listFile() {
startTime = time.Now()
fileInput, err := os.Open(fileName)
if err != nil {
log.Fatal(err)
}
defer fileInput.Close()
scanner := bufio.NewScanner(fileInput)
for scanner.Scan() {
for _, targetContent := range []string{"numpy", "grabscreen", "cv2", "time", "os", "pandas", "tqdm", "collections", "models", "random", "inception_v3", "googlenet", "shuffle", "getkeys", "tflearn", "directkeys", "statistics", "motion", "tflearn.layers.conv", "conv_2d", "max_pool_2d", "avg_pool_2d", "conv_3d", "max_pool_3d", "avg_pool_3d"} {
if strings.Contains(scanner.Text(), targetContent) {
if _, err := os.Stat(readDir + "output_" + strconv.Itoa(noFile)); os.IsNotExist(err) {
fmt.Println("File : " + readDir + "output_" + strconv.Itoa(noFile) + " does not exists, creating it now!")
createFile, err := os.Create(readDir + "output_" + strconv.Itoa(noFile))
if err != nil {
panic(err)
}
createFile.Close()
}
fileOutput, err := os.OpenFile(readDir+"output_"+strconv.Itoa(noFile), os.O_APPEND|os.O_WRONLY, 0600)
if err != nil {
panic(err)
}
if _, err = fileOutput.WriteString("contains : " + targetContent + " in : " + scanner.Text() + "\n"); err != nil {
panic(err)
}
fileOutput.Close()
fmt.Println("Writing file : ", readDir+"output_"+strconv.Itoa(noFile))
fmt.Println("contains : " + targetContent + " in : " + scanner.Text())
}
}
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
log.Printf("Execution took %s", time.Since(startTime))
}
}
func listFile() []string {
filesNames := make([]string, 0, 100)
files, err := ioutil.ReadDir(readDir)
if err != nil {
log.Fatal(err)
}
for _, f := range files {
if strings.HasSuffix(f.Name(), ".txt") {
fileName, err := filepath.Abs(string(f.Name()))
if err != nil {
log.Fatal(err)
}
filesNames = append(filesNames, fileName)
}
}
return filesNames
}

Is it possible to include an external file as a string constant in go?

I always wished it was possible to do something like this in C++:
const std::string fragmentShader = "
#include "shader.frag"
";
Obviously that doesn't work, and there is no way to do it in C++. But it is possible in go? i.e.
const fragmentShader string = `
<insert contents of shader.frag at compile-time>
`
The motivation should be obvious!
Not really, but here are a couple options:
Just take the contents of the file and put them in your source code:
const fragmentShaderFile = `contents of shader.frag`
You can embed assets with this tool: https://github.com/jteeuwen/go-bindata, but you'll have to re-embed the file anytime it's changed. (One could automate this as a git pre-commit hook though)
This is not possible in pure Go. You could however write a program that reads a file and creates a Go file from it, such as this:
package main
import "flag"
import "os"
import "fmt"
import "bufio"
import "io"
var (
packageName = flag.String("p", "main", "package name")
outFile = flag.String("o", "-", "output file. Defaults to stdout")
varName = flag.String("v", "file", "variable name")
)
const (
header = "package %s\n\nvar %s = [...]byte{\n"
trailer = "}\n"
)
func main() {
flag.Parse()
if len(flag.Args()) != 1 {
fmt.Fprintln(os.Stderr, "Please provide exactly one file name")
os.Exit(1)
}
var inF, outF *os.File
if *outFile == "-" {
outF = os.Stdout
} else {
var err error
outF, err = os.Create(*outFile)
if err != nil {
fmt.Fprintf(os.Stderr, "Cannot create %s: %v\n", *outFile, err)
os.Exit(1)
}
}
inF, err := os.Open(flag.Args()[0])
if err != nil {
fmt.Fprintf(os.Stderr, "Cannot open %s: %v\n", flag.Args()[0], err)
os.Exit(1)
}
in, out := bufio.NewReader(inF), bufio.NewWriter(outF)
fmt.Fprintf(out, header, *packageName, *varName)
buf := make([]byte, 16)
var n int
for n, err = io.ReadFull(in, buf); n > 0; n, err = io.ReadFull(in, buf) {
out.WriteRune('\t')
for i := 0; i < n-1; i++ {
fmt.Fprintf(out, "%#02x, ", buf[i])
}
fmt.Fprintf(out, "%#02x,\n", buf[n-1])
}
out.WriteString(trailer)
out.Flush()
if err != io.EOF {
fmt.Fprintf(os.Stderr, "An error occured while reading from %s: %v\n", flag.Args()[0], err)
os.Exit(1)
}
}

Reading from reader until a string is reached

I am trying to write a function to keep reading from a buffered reader until I hit a certain string, then to stop reading and return everything read prior to that string.
In other words, I want to do the same thing as reader.ReadString() does, except taking a string instead of a single byte.
For instance:
mydata, err := reader.ReadString("\r\n.\r\n") //obviously will not compile
How can I do this?
Thanks in advance,
Twichy
Amendment 1: Previous attempt
Here is my previous attempt; its badly written and doesnt work but hopefully it demonstrates what I am trying to do.
func readDotData(reader *bufio.Reader)(string, error){
delims := []byte{ '\r', '\n', '.', '\r', '\n'}
curpos := 0
var buffer []byte
for {
curpos = 0
data, err := reader.ReadSlice(delims[0])
if err!=nil{ return "", err }
buffer = append(buffer, data...)
for {
curpos++
b, err := reader.ReadByte()
if err!=nil{ return "", err }
if b!=delims[curpos]{
for curpos >= 0{
buffer = append(buffer, delims[curpos])
curpos--
}
break
}
if curpos == len(delims){
return string(buffer[len(buffer)-1:]), nil
}
}
}
panic("unreachable")
}
package main
import (
"bytes"
"fmt"
"log"
)
type reader interface {
ReadString(delim byte) (line string, err error)
}
func read(r reader, delim []byte) (line []byte, err error) {
for {
s := ""
s, err = r.ReadString(delim[len(delim)-1])
if err != nil {
return
}
line = append(line, []byte(s)...)
if bytes.HasSuffix(line, delim) {
return line[:len(line)-len(delim)], nil
}
}
}
func main() {
src := bytes.NewBufferString("123deli456elim789delimABCdelimDEF")
for {
b, err := read(src, []byte("delim"))
if err != nil {
log.Fatal(err)
}
fmt.Printf("%q\n", b)
}
}
Playground
Output:
"123deli456elim789"
"ABC"
2009/11/10 23:00:00 EOF
http://play.golang.org/p/BpA5pOc-Rn
package main
import (
"bytes"
"fmt"
)
func main() {
b := bytes.NewBuffer([]byte("Hello, playground!\r\n.\r\nIrrelevant trailer."))
c := make([]byte, 0, b.Len())
for {
p := b.Bytes()
if bytes.Equal(p[:5], []byte("\r\n.\r\n")) {
fmt.Println(string(c))
return
}
c = append(c, b.Next(1)...)
}
}
For example,
package main
import (
"bufio"
"bytes"
"fmt"
"strings"
)
var delim = []byte{'\r', '\n', '.', '\r', '\n'}
func ScanLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
for i := 0; i+len(delim) <= len(data); {
j := i + bytes.IndexByte(data[i:], delim[0])
if j < i {
break
}
if bytes.Equal(data[j+1:j+len(delim)], delim[1:]) {
// We have a full delim-terminated line.
return j + len(delim), data[0:j], nil
}
i = j + 1
}
// If we're at EOF, we have a final, non-terminated line. Return it.
if atEOF {
return len(data), data, nil
}
// Request more data.
return 0, nil, nil
}
func main() {
delims := string(delim)
input := "1234" + delims + "5678" + delims + "1234567901234567890" + delims
scanner := bufio.NewScanner(strings.NewReader(input))
scanner.Split(ScanLines)
for scanner.Scan() {
fmt.Printf("%s\n", scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Printf("Invalid input: %s", err)
}
}
Output:
1234
5678
1234567901234567890
Because you have the same byte in the string, you can do it as below:
func readWithEnd(reader *bufio.Reader) ([]byte, error) {
message, err := reader.ReadBytes('#')
if err != nil {
return nil, err
}
a1, err := reader.ReadByte()
if err != nil {
return nil, err
}
message = append(message, a1)
if a1 != '\t' {
message2, err := readWithEnd(reader)
if err != nil {
return nil, err
}
ret := append(message, message2...)
return ret, nil
}
a2, err := reader.ReadByte()
if err != nil {
return nil, err
}
message = append(message, a2)
if a2 != '#' {
message2, err := readWithEnd(reader)
if err != nil {
return nil, err
}
ret := append(message, message2...)
return ret, nil
}
return message, nil
}
This is the sample that can recognize the "#\t#" in TCP connection

Resources