I want to read a text file character by character and print invalid input for those characters that exceed "H"and numbers that exceed "8".
eg: my input is
I9,A2
A10,C3
D2,L3
output:
invalid input for all three
package main
import (
"bufio"
"fmt"
"log"
"os"
)
func readLines(path string) ([]string, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
var lines []string
scanner := bufio.NewScanner(file)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
return lines, scanner.Err()
}
func main() {
lines, err := readLines("chessin.txt")
if err != nil {
log.Fatalf("readLines: %s", err)
}
var numLines int = len(lines)
for i := 0; i < numLines; i++ {
for j := 0; j < len(lines[i]); j++ {
if j > 'H' {
fmt.Printf("invalid input")
}
}
}
}
You need to edit the inner loop to check for every input line,
and find the number then comma and so, like this working sample code:
package main
import (
"bufio"
"fmt"
"log"
"os"
"strconv"
"strings"
)
func readLines(path string) ([]string, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
var lines []string
scanner := bufio.NewScanner(file)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
return lines, scanner.Err()
}
func checkOne(letter byte, number, i, j int) {
if letter > 'H' {
fmt.Printf("invalid input %q # (%d, %d) \n", letter, i, j)
}
if number > 8 {
fmt.Printf("invalid input number %d # (%d, %d) \n", number, i, j+1)
}
}
func main() {
lines, err := readLines("chessin.txt")
if err != nil {
log.Fatalf("readLines: %s", err)
}
var numLines int = len(lines)
for i := 0; i < numLines; i++ {
line := lines[i]
j := 0
comma := strings.IndexByte(line, ',')
if comma == -1 {
log.Fatalf("comma not found at line: %d", i)
}
number, err := strconv.Atoi(line[j+1 : comma])
if err != nil {
log.Fatalf("line:%d err: %s", i, err)
}
checkOne(line[j], number, i, j)
j = comma + 1
number, err = strconv.Atoi(line[j+1:])
if err != nil {
log.Fatalf("line:%d err: %s", i, err)
}
checkOne(line[j], number, i, j)
}
}
input file "chessin.txt":
I9,A2
A10,C3
D2,L3
output:
invalid input 'I' # (0, 0)
invalid input number 9 # (0, 1)
invalid input number 10 # (1, 1)
invalid input 'L' # (2, 3)
You can simplify by using string split and slice. Do not read and store all the values in an array and then loop through them again. Assuming the first character is always Caps and there will be no negative values. Working sample code is as follows:
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
file, err := os.Open("chessin.txt")
if err != nil {
fmt.Println(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
valid := []bool{}
for scanner.Scan() {
strs := strings.Split(scanner.Text(), ",")
valid = append(valid, validateStrings(strs))
}
fmt.Println(valid)
}
func validateStrings(strs []string) bool {
for _, str := range strs {
char := str[0]
num, err := strconv.Atoi(string(str[1:len(str)]))
if err != nil {
fmt.Println("Invalid input")
os.Exit(1)
}
if char < 'A' || char > 'H' || num > 8 || num < 0 {
return false
}
}
return true
}
Related
I am downloading a large file in concurrent Chunks of 10MB using GO as shown below.
package main
import (
"fmt"
"io/ioutil"
"net/http"
"strconv"
)
func main() {
chunkSize := 1024 * 1024 * 10 // 10MB
url := "http://path/to/large/zip/file/zipfile.zip"
filepath := "zipfile.zip"
res, _ := http.Head(url)
maps := res.Header
length, _ := strconv.Atoi(maps["Content-Length"][0]) // Get the content length from the header request
// startByte and endByte determines the positions of the chunk that should be downloaded
var startByte = 0
var endByte = chunkSize - 1
for startByte < length {
if endByte > length {
endByte = length - 1
}
go func(startByte, endByte int) {
client := &http.Client {}
req, _ := http.NewRequest("GET", url, nil)
rangeHeader := fmt.Sprintf("bytes=%d-%d", startByte, endByte)
req.Header.Add("Range", rangeHeader)
resp,_ := client.Do(req)
defer resp.Body.Close()
data, _ := ioutil.ReadAll(resp.Body)
addToFile(filepath, startByte, endByte, data)
}(startByte, endByte)
startByte = endByte + 1
endByte += chunkSize
}
}
func addToFile(filepath string, startByte, endByte int, data []byte) {
// TODO: write to byte range in file
}
How should I go about creating the file, and writing to a specified byte range within the file corresponding to the byte range of the chunk?
For example, if I get the data from the bytes 262144000-272629759, the addToFile function should write to 262144000-272629759 within the zipfile.zip. Then, if data from another range is obtained, that should be written to the respective range in zipfile.zip.
Figured out how to do this. Change the addToFile function as shown below.
func addToFile(filepath string, startByte int, data []byte) {
f, err := os.OpenFile(filepath, os.O_CREATE | os.O_WRONLY, os.ModeAppend)
if err != nil {
panic("File not found")
}
whence := io.SeekStart
_, err = f.Seek(int64(startByte), whence)
f.Write(data)
f.Sync() //flush to disk
f.Close()
}
For example,
package main
import (
"fmt"
"io"
"io/ioutil"
"os"
)
func write(ws io.WriteSeeker, offset int64, p []byte) (n int, err error) {
_, err = ws.Seek(offset, io.SeekStart)
if err != nil {
return 0, err
}
n, err = ws.Write(p)
if err != nil {
return 0, err
}
return n, nil
}
func main() {
filename := `test.file`
f, err := os.Create(filename)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
defer f.Close()
buf := make([]byte, 16)
for i := range buf {
buf[i] = byte('A' + i)
}
_, err = write(f, 16, buf)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
for i := range buf {
buf[i] = byte('a' + i)
}
_, err = write(f, 0, buf)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
err = f.Close()
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
data, err := ioutil.ReadFile(filename)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
fmt.Printf("%q\n", data)
}
Output:
"abcdefghijklmnopABCDEFGHIJKLMNOP"
This is my first Go program. I'm learning the language but it's a bit difficult to understand all the concepts so in order to practice I wrote a code to detect same file. It's a simple program which recursively check for duplicated files in a directory.
but:
how to detect duplicate file in directory files
the matter isn't directory recursively. the matter is how to compare
You could take the hash of each file body and then compare the hashes in a dictionary/map.
package main
import (
"crypto/md5"
"fmt"
"io"
"io/ioutil"
"log"
"os"
)
func main() {
contentHashes := make(map[string]string)
if err := readDir("./", contentHashes); err != nil {
log.Fatal(err)
}
}
func readDir(dirName string, contentHashes map[string]string) (err error) {
filesInfos, err := ioutil.ReadDir(dirName)
if err != nil {
return
}
for _, fi := range filesInfos {
if fi.IsDir() {
err := readDir(dirName+fi.Name()+"/", contentHashes)
if err != nil {
return err
}
} else {
// The important bits for this question
location := dirName + fi.Name()
// open the file
f, err := os.Open(location)
if err != nil {
return err
}
h := md5.New()
// copy the file body into the hash function
if _, err := io.Copy(h, f); err != nil {
return err
}
// Check if a file body with the same hash already exists
key := fmt.Sprintf("%x", h.Sum(nil))
if val, exists := contentHashes[key]; exists {
fmt.Println("Duplicate found", val, location)
} else {
contentHashes[key] = location
}
}
}
return
}
use sha256 to compare files
example:
package main
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"os"
"path/filepath"
"sync"
"flag"
"runtime"
"io"
)
var dir string
var workers int
type Result struct {
file string
sha256 [32]byte
}
func worker(input chan string, results chan<- *Result, wg *sync.WaitGroup) {
for file := range input {
var h = sha256.New()
var sum [32]byte
f, err := os.Open(file)
if err != nil {
fmt.Fprintln(os.Stderr, err)
continue
}
if _, err = io.Copy(h, f); err != nil {
fmt.Fprintln(os.Stderr, err)
f.Close()
continue
}
f.Close()
copy(sum[:], h.Sum(nil))
results <- &Result{
file: file,
sha256: sum,
}
}
wg.Done()
}
func search(input chan string) {
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
fmt.Fprintln(os.Stderr, err)
} else if info.Mode().IsRegular() {
input <- path
}
return nil
})
close(input)
}
func main() {
flag.StringVar(&dir, "dir", ".", "directory to search")
flag.IntVar(&workers, "workers", runtime.NumCPU(), "number of workers")
flag.Parse()
fmt.Printf("Searching in %s using %d workers...\n", dir, workers)
input := make(chan string)
results := make(chan *Result)
wg := sync.WaitGroup{}
wg.Add(workers)
for i := 0; i < workers; i++ {
go worker(input, results, &wg)
}
go search(input)
go func() {
wg.Wait()
close(results)
}()
counter := make(map[[32]byte][]string)
for result := range results {
counter[result.sha256] = append(counter[result.sha256], result.file)
}
for sha, files := range counter {
if len(files) > 1 {
fmt.Printf("Found %d duplicates for %s: \n", len(files), hex.EncodeToString(sha[:]))
for _, f := range files {
fmt.Println("-> ", f)
}
}
}
}
How to rename a file in unicode format, the gaps?
When you try to rename the standard library function, the file is not located.
Old: /usr/home/spouk/go/src/simple/Agraba - I Know You Are Smiling Now (Original Mix).mp4 New: /usr/home/spouk/go/src/simple/Agraba_-_I_Know_You_Are_Smiling_Now__Original_Mix_.mp4 [RENAME] rename Agraba - I Know You Are Smiling Now (Original Mix).mp4 /usr/home/spouk/go/src/simple/Agraba_-_I_Know_You_Are_Smiling_Now__Original_Mix_.mp4: no such file or directory
Someone tell me what to use or where to get acquainted with the direction of solution to this problem. Tnx.
package main
import (
"flag"
d "github.com/fiam/gounidecode/unidecode"
"fmt"
"path/filepath"
"os"
"strings"
"strconv"
"os/exec"
)
type (
File struct {
OriginPath string
ConvertPath string
}
FilesStack struct {
Files []File
InPath string
OutPath string
}
)
func NewFilesStack() *FilesStack {
n := new(FilesStack)
n.Files = []File{}
return n
}
func (f *FilesStack) RuneToAscii(r rune) string {
if r < 128 {
return string(r)
} else {
return "\\u" + strconv.FormatInt(int64(r), 16)
}
}
func (f *FilesStack) TransliterCyr(str string) string {
var result []string
for _, sym := range d.Unidecode(str) {
if (f.RuneToAscii(sym) == " ") || (f.RuneToAscii(sym) == "'") || (f.RuneToAscii(sym) == "`") || (f.RuneToAscii(sym) == ")") || (f.RuneToAscii(sym) == "(") {
result = append(result, "_")
} else {
result = append(result, f.RuneToAscii(sym))
}
}
return strings.Join(result, "")
}
func (f *FilesStack) walkFunction(path string, info os.FileInfo, err error) error {
if !info.IsDir() {
res, err := filepath.Abs(filepath.Dir(info.Name()))
if err != nil {
fmt.Printf(err.Error())
}
ext := filepath.Ext(info.Name())
if ext == ".mp4" {
file := new(File)
//make new file name
oldfile := filepath.Join(res, info.Name())
newname := filepath.Join(res, f.TransliterCyr(info.Name()))
file.OriginPath = filepath.Join(res, newname)
fmt.Printf("Old: %s\nNew: %s\n", oldfile, newname)
//rename file
if err_rename := os.Rename(info.Name(), newname); err_rename != nil {
fmt.Printf("[RENAME] %s\n", err_rename.Error())
}
tmpname := []string{}
name := strings.TrimRight(info.Name(), filepath.Ext(info.Name()))
for _, sym := range name {
if f.RuneToAscii(sym) != " " {
tmpname = append(tmpname, string(sym))
}
}
word := strings.Join(tmpname, "")
oo, _ := filepath.Abs(f.OutPath)
rr := strings.Join([]string{f.TransliterCyr(word), ".mp3"}, "")
//fmt.Printf("Res: %v %v\n", rr, word)
file.ConvertPath = filepath.Join(oo, rr)
//fmt.Printf("Infile: %v\n", file.OriginPath)
//fmt.Printf("Outfile: %v\n", file.ConvertPath)
f.Files = append(f.Files, *file)
}
}
return nil
}
func (f *FilesStack) ParseFilesmp4() {
//парсинг файлов
if err := filepath.Walk(f.InPath, f.walkFunction); err != nil {
fmt.Printf(err.Error())
}
//конвертация
for _, file := range f.Files {
fmt.Printf("Start convertation %s \n", file.OriginPath)
command := fmt.Sprintf("ffmpeg -i %s -f mp3 %s", file.OriginPath, file.ConvertPath)
//fmt.Printf("Command %v\n", command)
cmd, err_convert := exec.Command(command).Output()
if err_convert != nil {
fmt.Printf(err_convert.Error())
}
fmt.Printf("Result convert: %v\n", cmd)
}
}
// note, that variables are pointers
var (
inpath = flag.String("inpath", ".", "директория для поиска всех файлов с расширением .mp4")
outpath = flag.String("outpath", ".", "директория для сохранения .mp3")
)
func main() {
//make files structy
f := NewFilesStack()
//parse arguments
flag.Parse()
fmt.Printf("inpath: %s\noutpath: %s\n", *inpath, *outpath)
//set arguments
f.InPath = *inpath
f.OutPath = *outpath
f.ParseFilesmp4()
}
How to read data from file in Google Go when my data file looks like this:
SomeString 200.0 2
OtherString 100.6 9
OneMoreString 550.8 1
(String, double and integer). I tried bufio.readLine and bufio.readString but these are reading whole line, while i need seperated variables.
Something like this might work--if you've got spaces in your string, you might need to handle that differently:
package main
import (
"fmt"
"os"
)
func main() {
f, err := os.Open("scan.txt")
if err != nil {
fmt.Println(err)
}
for {
var i int
var flt float64
var str string
var n int
n, err = fmt.Fscanln(f, &str, &flt, &i)
if n == 0 || err != nil {
break
}
fmt.Println("string:", str, "; float:", flt, "; int:", i)
}
}
outputs:
string: SomeString ; float: 200 ; int: 2
string: OtherString ; float: 100.6 ; int: 9
string: OneMoreString ; float: 550.8 ; int: 1
For example,
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
type Data struct {
AString string
AFloat float64
AnInt int64
}
func ParseLine(line string) (*Data, error) {
data := new(Data)
var err error
text := strings.TrimRight(line, " ")
i := strings.LastIndex(text, " ")
i++
text = text[i:]
data.AnInt, err = strconv.ParseInt(text, 10, 64)
if err != nil {
return nil, err
}
line = line[:i]
text = strings.TrimRight(line, " ")
i = strings.LastIndex(text, " ")
i++
text = text[i:]
data.AFloat, err = strconv.ParseFloat(text, 64)
if err != nil {
return nil, err
}
line = line[:i]
data.AString = line
return data, nil
}
func main() {
f, err := os.Open("data.txt")
if err != nil {
fmt.Fprintln(os.Stderr, "opening input:", err)
return
}
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
data, err := ParseLine(line)
if err != nil {
fmt.Fprintln(os.Stderr, "reading input:", err, ":", line)
continue
}
fmt.Println(*data)
}
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "reading input:", err)
}
}
Input (data.txt):
Some String 200.0 2
Other String 100.6 9
One More String 550.8 1
Output:
{Some String 200 2}
{Other String 100.6 9}
{One More String 550.8 1}
I am trying to write a go function that will read in lines in a text file, sort them (alphabetize), and overwrite them back to the file. Right now, I am able to essentially emulate cat, but I can't seem to be able to manipulate the contents of the elements in read_line.
func sort() {
ff, _ := os.OpenFile(file, os.O_RDWR, 0666)
f := bufio.NewReader(ff)
for {
read_line, _ := f.ReadString('\n')
fmt.Print(read_line)
if read_line == "" {
break
}
}
ff.Close()
}
when i use ReadString, how can i store each line into a slice (or is there a better way to store them so i can manipulate them)? Then I would use the sort package in a manner similar to this:
sorted := sort.Strings(lines)
then, to write to the file, i am using something similar to the following, although i have not included it because i have not yet gotten "sort" to work:
io.WriteString(ff, (lines + "\n"))
Thank you in advance for any suggestions!
For example,
package main
import (
"bufio"
"fmt"
"os"
"sort"
)
func readLines(file string) (lines []string, err os.Error) {
f, err := os.Open(file)
if err != nil {
return nil, err
}
defer f.Close()
r := bufio.NewReader(f)
for {
const delim = '\n'
line, err := r.ReadString(delim)
if err == nil || len(line) > 0 {
if err != nil {
line += string(delim)
}
lines = append(lines, line)
}
if err != nil {
if err == os.EOF {
break
}
return nil, err
}
}
return lines, nil
}
func writeLines(file string, lines []string) (err os.Error) {
f, err := os.Create(file)
if err != nil {
return err
}
defer f.Close()
w := bufio.NewWriter(f)
defer w.Flush()
for _, line := range lines {
_, err := w.WriteString(line)
if err != nil {
return err
}
}
return nil
}
func main() {
file := `lines.txt`
lines, err := readLines(file)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
sort.Strings(lines)
err = writeLines(file, lines)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
This is a pretty simple way of doing it.
import (
"bytes"
"io/ioutil"
"sort"
)
// allow [][]byte to implement the sort.Interface interface
type lexicographically [][]byte
// bytes.Compare compares the byte slices lexicographically (alphabetically)
func (l lexicographically) Less(i, j int) bool { return bytes.Compare(l[i], l[j]) < 0 }
func (l lexicographically) Len() int { return len(l) }
func (l lexicographically) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func SortFile(name string) error {
content, err := ioutil.ReadFile(name)
if err != nil {
return err
}
lines := bytes.Split(content, []byte{'\n'})
sort.Sort(lexicographically(lines))
content = bytes.Join(lines, []byte{'\n'})
return ioutil.WriteFile(name, content, 0644)
}
since you are about to sort the lines, you pretty much need to read the entire file. you can either slurp the file with io/ioutil.ReadAll or you can just write a small slurp function. once you have the lines of the file, sorting them can be done with a call to sort.Strings. i'll add a perhaps overly verbose version which hopefully illustrates how it can be done. i also recomment reading this excellent explanation on how go's sort package works: Go's sort package
package main
import (
"os"
"bufio"
"fmt"
"sort"
)
// slurp file into slice of lines/strings
func slurp(f string) (lines []string, e os.Error) {
var fd *os.File
var line string
var bufRd *bufio.Reader
var keepReading bool = true
fd, e = os.Open(f)
if e != nil {
return nil, e
}
defer fd.Close()
bufRd = bufio.NewReader(fd)
for keepReading {
line, e = bufRd.ReadString('\n')
switch e {
case nil:
lines = append(lines, line)
case os.EOF:
lines = append(lines, line)
keepReading = false
default:
return lines, e
}
}
return lines, nil
}
// test stuff out..
func main() {
if len(os.Args) > 1 {
lines, e := slurp(os.Args[1])
if e != nil {
fmt.Fprintf(os.Stderr,"%s\n", e)
os.Exit(1)
}
fmt.Println("\n----- unsorted -----\n")
for _, line := range lines {
fmt.Printf("%s", line)
}
fmt.Println("\n----- sorted -----\n")
sort.Strings(lines)
for _, line := range lines {
fmt.Printf("%s", line)
}
}
}
note that the sort is in-place, so it does not return anything
Just wondering how convenient is using Unix's sort for this purpose. I know it's not possible to have this code working in many deployment scenarios, but I see it worth it to mention as an option:
package main
import (
"os"
"os/exec"
)
func main() {
file := "file.txt"
command := []string{"sort", file, "-o", file}
cmd := exec.Command(command[0], command[1:]...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
panic(err)
}
}
Thoughts?