diff --git a/part_5/5.1/1.go b/part_5/5.1/1.go new file mode 100644 index 0000000..76c4e2a --- /dev/null +++ b/part_5/5.1/1.go @@ -0,0 +1,16 @@ +package main + +import ( + "fmt" + "log" + "os" +) + +func main() { + file, err := os.Create("test.txt") + if err != nil { + log.Fatal(err) + } + fmt.Println(file) + file.Close() // закрытие файла +} diff --git a/part_5/5.1/10.go b/part_5/5.1/10.go new file mode 100644 index 0000000..530d620 --- /dev/null +++ b/part_5/5.1/10.go @@ -0,0 +1,15 @@ +package main + +import ( + "fmt" + "log" + "os" +) + +func main() { + data, err := os.ReadFile("pirates.txt") + if err != nil { + log.Fatal(err) + } + fmt.Println(string(data)) +} diff --git a/part_5/5.1/11.go b/part_5/5.1/11.go new file mode 100644 index 0000000..df6854d --- /dev/null +++ b/part_5/5.1/11.go @@ -0,0 +1,25 @@ +package main + +import ( + "bufio" + "fmt" + "log" + "os" +) + +func main() { + file, err := os.Open("pirates.txt") + if err != nil { + log.Fatal(err) + } + defer file.Close() + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + fmt.Println(scanner.Text()) + } + + if err := scanner.Err(); err != nil { + log.Fatal(err) + } +} diff --git a/part_5/5.1/12.go b/part_5/5.1/12.go new file mode 100644 index 0000000..29f11b4 --- /dev/null +++ b/part_5/5.1/12.go @@ -0,0 +1,26 @@ +package main + +import ( + "bufio" + "fmt" + "log" + "os" +) + +func main() { + file, err := os.Open("pirates.txt") + if err != nil { + log.Fatal(err) + } + defer file.Close() + + scanner := bufio.NewScanner(file) + scanner.Split(bufio.ScanWords) + for scanner.Scan() { + fmt.Println(scanner.Text()) + } + + if err := scanner.Err(); err != nil { + log.Fatal(err) + } +} diff --git a/part_5/5.1/13.go b/part_5/5.1/13.go new file mode 100644 index 0000000..4a8861d --- /dev/null +++ b/part_5/5.1/13.go @@ -0,0 +1,19 @@ +package main + +import ( + "log" + "os" +) + +func main() { + firstText := "Пятнадцать человек на сундук мертвеца,\n" + secondText := "Йо-хо-хо, и бутылка рома,\n" + file, err := os.OpenFile("pirates2.txt", os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + log.Fatal(err) + } + defer file.Close() + + file.WriteString(firstText) + file.WriteString(secondText) +} diff --git a/part_5/5.1/14.go b/part_5/5.1/14.go new file mode 100644 index 0000000..014b2ed --- /dev/null +++ b/part_5/5.1/14.go @@ -0,0 +1,19 @@ +package main + +import ( + "log" + "os" +) + +func main() { + firstText := "Пей, и дьявол тебя доведет до конца,\n" + secondText := "Йо-хо-хо, и бутылка рома!" + file, err := os.OpenFile("pirates2.txt", os.O_WRONLY, 0666) + if err != nil { + log.Fatal(err) + } + defer file.Close() + + file.WriteString(firstText) + file.WriteString(secondText) +} diff --git a/part_5/5.1/15.go b/part_5/5.1/15.go new file mode 100644 index 0000000..6a2f848 --- /dev/null +++ b/part_5/5.1/15.go @@ -0,0 +1,22 @@ +package main + +import ( + "fmt" + "io" + "log" + "os" +) + +func main() { + file, err := os.OpenFile("pirates2.txt", os.O_RDONLY, 0666) + if err != nil { + log.Fatal(err) + } + defer file.Close() + + data, err := io.ReadAll(file) + if err != nil { + log.Fatal(err) + } + fmt.Println(string(data)) +} diff --git a/part_5/5.1/16.go b/part_5/5.1/16.go new file mode 100644 index 0000000..ff1390c --- /dev/null +++ b/part_5/5.1/16.go @@ -0,0 +1,34 @@ +package main + +import ( + "fmt" + "io" + "log" + "os" +) + +func main() { + originalFile, err := os.Open("pirates2.txt") + if err != nil { + log.Fatal(err) + } + defer originalFile.Close() + + newFile, err := os.Create("pirates2_copy.txt") + if err != nil { + log.Fatal(err) + } + defer newFile.Close() + + bytesWritten, err := io.Copy(newFile, originalFile) + if err != nil { + log.Fatal(err) + } + fmt.Printf("Copied %d bytes", bytesWritten) // Copied 224 bytes + + // Фиксируем содержимое файла и осуществляем освобождение буфера + err = newFile.Sync() + if err != nil { + log.Fatal(err) + } +} diff --git a/part_5/5.1/17.go b/part_5/5.1/17.go new file mode 100644 index 0000000..5c76c24 --- /dev/null +++ b/part_5/5.1/17.go @@ -0,0 +1,45 @@ +package main + +import ( + "fmt" + "log" + "os" +) + +type Car struct { + model string + age uint8 + weight float64 + number string +} + +func createCars() []Car { + return []Car{ + { + model: "Oka", + weight: 13.22, + number: "ak1245j76", + age: 13, + }, + { + model: "Lada7", + weight: 23.14, + number: "ak2147j175", + age: 1, + }, + } +} + +func main() { + myFile, err := os.Create("car_fprintln.txt") + if err != nil { + log.Fatal(err) + } + defer myFile.Close() + + myCar := createCars() + for _, it := range myCar { + n, _ := fmt.Fprintln(myFile, it.model, it.number, it.age, it.weight) + fmt.Println("Записано: ", n, "байт") + } +} diff --git a/part_5/5.1/18.go b/part_5/5.1/18.go new file mode 100644 index 0000000..a9c8bba --- /dev/null +++ b/part_5/5.1/18.go @@ -0,0 +1,46 @@ +package main + +import ( + "fmt" + "log" + "os" +) + +type Car struct { + model string + age uint8 + weight float64 + number string +} + +func createCars() []Car { + return []Car{ + { + model: "Oka", + weight: 13.22, + number: "ak1245j76", + age: 13, + }, + { + model: "Lada7", + weight: 23.14, + number: "ak2147j175", + age: 1, + }, + } +} + +func main() { + myFile, err := os.Create("car_fprintf.txt") + if err != nil { + log.Fatal(err) + } + defer myFile.Close() + + myCar := createCars() + for _, it := range myCar { + n, _ := fmt.Fprintf(myFile, "Model: %s ||Number: %s ||Age: %d ||Weight: %f\n", + it.model, it.number, it.age, it.weight) + fmt.Println("Записано: ", n, "байт") + } +} diff --git a/part_5/5.1/19.go b/part_5/5.1/19.go new file mode 100644 index 0000000..b8bb343 --- /dev/null +++ b/part_5/5.1/19.go @@ -0,0 +1,39 @@ +package main + +import ( + "fmt" + "io" + "log" + "os" +) + +type Car struct { + model string + age uint8 + weight float64 + number string +} + +func main() { + myFile, err := os.Open("car_fprintln.txt") + if err != nil { + log.Fatal(err) + } + defer myFile.Close() + + myCars := []Car{} + + for { + var car Car + n, err := fmt.Fscanln(myFile, &car.model, &car.number, &car.age, &car.weight) + if err != nil { + if err == io.EOF { // Читаем до конца файла + break + } + log.Fatal(err) + } + fmt.Println("Прочитано: ", n, "элемента(-ов)") + myCars = append(myCars, car) + } + fmt.Println(myCars) +} diff --git a/part_5/5.1/2.go b/part_5/5.1/2.go new file mode 100644 index 0000000..5f817ad --- /dev/null +++ b/part_5/5.1/2.go @@ -0,0 +1,19 @@ +package main + +import ( + "fmt" + "log" + "os" +) + +func main() { + fileInfo, err := os.Stat("test.txt") + if err != nil { + log.Fatal(err) + } + fmt.Println("File name:", fileInfo.Name()) + fmt.Println("Size in bytes:", fileInfo.Size()) + fmt.Println("Permissions:", fileInfo.Mode()) + fmt.Println("Last modified:", fileInfo.ModTime()) + fmt.Println("Is Directory: ", fileInfo.IsDir()) +} diff --git a/part_5/5.1/20.go b/part_5/5.1/20.go new file mode 100644 index 0000000..01a34a7 --- /dev/null +++ b/part_5/5.1/20.go @@ -0,0 +1,40 @@ +package main + +import ( + "fmt" + "io" + "log" + "os" +) + +type Car struct { + model string + age uint8 + weight float64 + number string +} + +func main() { + myFile, err := os.Open("car_fprintf.txt") + if err != nil { + log.Fatal(err) + } + defer myFile.Close() + + myCars := []Car{} + + for { + var car Car + n, err := fmt.Fscanf(myFile, "Model: %s ||Number: %s ||Age: %d ||Weight: %f\n", + &car.model, &car.number, &car.age, &car.weight) + if err != nil { + if err == io.EOF || err.Error() == "unexpected EOF" { + break + } + log.Fatal(err) + } + fmt.Println("Прочитано: ", n, "элемента(-ов)") + myCars = append(myCars, car) + } + fmt.Println(myCars) +} diff --git a/part_5/5.1/3.go b/part_5/5.1/3.go new file mode 100644 index 0000000..0979981 --- /dev/null +++ b/part_5/5.1/3.go @@ -0,0 +1,17 @@ +package main + +import ( + "log" + "os" +) + +func main() { + originalPath := "test.txt" + newPath := "../test2.txt" // переместит файл на уровень выше с именем test2.txt + // можно также оставить старое имя у перемещаемого файла - test.txt + // newPath := "test2.txt" // переименование файла в текущей директории + err := os.Rename(originalPath, newPath) + if err != nil { + log.Fatal(err) + } +} diff --git a/part_5/5.1/4.go b/part_5/5.1/4.go new file mode 100644 index 0000000..d0bed69 --- /dev/null +++ b/part_5/5.1/4.go @@ -0,0 +1,14 @@ +package main + +import ( + "log" + "os" +) + +func main() { + pathToFile := "test.txt" + err := os.Remove(pathToFile) + if err != nil { + log.Fatal(err) + } +} diff --git a/part_5/5.1/5.go b/part_5/5.1/5.go new file mode 100644 index 0000000..b099084 --- /dev/null +++ b/part_5/5.1/5.go @@ -0,0 +1,20 @@ +package main + +import ( + "fmt" + "log" + "os" +) + +func main() { + pathToFile := "pirates.txt" + fileInfo, err := os.Stat(pathToFile) + if err != nil { + if os.IsNotExist(err) { + log.Fatal("File does not exist.") + os.Exit(1) // выход из приложения со статусом 1 + } + } + fmt.Println("File does exist!") + fmt.Println("File information:", fileInfo) +} diff --git a/part_5/5.1/6.go b/part_5/5.1/6.go new file mode 100644 index 0000000..f2006bb --- /dev/null +++ b/part_5/5.1/6.go @@ -0,0 +1,23 @@ +package main + +import ( + "fmt" + "log" + "os" +) + +func main() { + firstText := "Пятнадцать человек на сундук мертвеца,\n" + secondText := "Йо-хо-хо, и бутылка рома," + file, err := os.Create("pirates.txt") + if err != nil { + log.Fatal(err) + } + defer file.Close() + + // функция WriteString преобразует string в []byte, после чего записывает в файл + file.WriteString(firstText) + // ручное приведение типа string в []byte и запись данных в файл + file.Write([]byte(secondText)) + fmt.Println("Text was wrote to file") +} diff --git a/part_5/5.1/7.go b/part_5/5.1/7.go new file mode 100644 index 0000000..5b7f4ba --- /dev/null +++ b/part_5/5.1/7.go @@ -0,0 +1,29 @@ +package main + +import ( + "fmt" + "io" + "log" + "os" +) + +func main() { + file, err := os.Open("pirates.txt") + if err != nil { + log.Fatal(err) + } + defer file.Close() + + file.WriteString("jgjgjg") // ничего в файл не добавится!!! + data := make([]byte, 128) + for { + length, err := file.Read(data) + fmt.Printf("Reading %d byte\n", length) + if err == io.EOF { // достигли конца файла? + break + } + } + fmt.Println(string(data)) + + fmt.Println("Text was read from file") +} diff --git a/part_5/5.1/8.go b/part_5/5.1/8.go new file mode 100644 index 0000000..d8e425a --- /dev/null +++ b/part_5/5.1/8.go @@ -0,0 +1,28 @@ +package main + +import ( + "fmt" + "io" + "log" + "os" +) + +func main() { + file, err := os.Open("pirates.txt") + if err != nil { + log.Fatal(err) + } + defer file.Close() + + tempData := make([]byte, 32) + data := []byte{} + for { + length, err := file.Read(tempData) + fmt.Printf("Reading %d byte\n", length) + if err == io.EOF { // достигли конца файла? + break + } + data = append(data, tempData...) + } + fmt.Println(string(data)) +} diff --git a/part_5/5.1/9.go b/part_5/5.1/9.go new file mode 100644 index 0000000..e5b6201 --- /dev/null +++ b/part_5/5.1/9.go @@ -0,0 +1,22 @@ +package main + +import ( + "fmt" + "io/ioutil" + "log" + "os" +) + +func main() { + file, err := os.Open("pirates.txt") + if err != nil { + log.Fatal(err) + } + defer file.Close() + + data, err := ioutil.ReadAll(file) + if err != nil { + log.Fatal(err) + } + fmt.Println(string(data)) +} diff --git a/part_5/5.2/1.go b/part_5/5.2/1.go new file mode 100644 index 0000000..111f707 --- /dev/null +++ b/part_5/5.2/1.go @@ -0,0 +1,15 @@ +package main + +import ( + "fmt" + "log" + "os" +) + +func main() { + err := os.Mkdir("myDir", 0777) + if err != nil { + log.Fatal(err) + } + fmt.Println("Directory created") +} diff --git a/part_5/5.2/10.go b/part_5/5.2/10.go new file mode 100644 index 0000000..5c269f5 --- /dev/null +++ b/part_5/5.2/10.go @@ -0,0 +1,26 @@ +package main + +import ( + "fmt" + "io/fs" + "path/filepath" +) + +func main() { + countDirs := 0 + countFiles := 0 + filepath.WalkDir("C:\\Go", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if !d.IsDir() { + countFiles++ + } else { + countDirs++ + } + return nil + }) + + fmt.Printf("Amount of files: %d\n", countFiles) + fmt.Printf("Amount of directories: %d\n", countDirs) +} diff --git a/part_5/5.2/11.go b/part_5/5.2/11.go new file mode 100644 index 0000000..ee040f8 --- /dev/null +++ b/part_5/5.2/11.go @@ -0,0 +1,22 @@ +package main + +import ( + "log" + "os" +) + +func createDirWithFile() { + os.Mkdir("myDir", 0777) + myFile, err := os.Create("myDir\\test.dat") + if err != nil { + log.Fatal(err) + } + defer myFile.Close() +} + +func main() { + createDirWithFile() + os.Rename("myDir", "newDir") // переименование + createDirWithFile() + os.Rename("myDir", "newDir\\myDir") // перемещение +} diff --git a/part_5/5.2/12.go b/part_5/5.2/12.go new file mode 100644 index 0000000..89df9c0 --- /dev/null +++ b/part_5/5.2/12.go @@ -0,0 +1,18 @@ +package main + +import ( + "fmt" + "os" +) + +func main() { + + pathToDir := "C:\\Go" + + if _, err := os.Stat(pathToDir); os.IsNotExist(err) { + // обработка ситуации, когда директории не существует + fmt.Println("Directory isn't exist") + } else { + fmt.Println("Directory already exists") + } +} diff --git a/part_5/5.2/2.go b/part_5/5.2/2.go new file mode 100644 index 0000000..b6b4272 --- /dev/null +++ b/part_5/5.2/2.go @@ -0,0 +1,15 @@ +package main + +import ( + "fmt" + "log" + "os" +) + +func main() { + err := os.Remove("myDir") + if err != nil { + log.Fatal(err) + } + fmt.Println("Directory removed") +} diff --git a/part_5/5.2/3.go b/part_5/5.2/3.go new file mode 100644 index 0000000..1fb6f5a --- /dev/null +++ b/part_5/5.2/3.go @@ -0,0 +1,48 @@ +package main + +import ( + "fmt" + "log" + "os" + "path/filepath" +) + +const tempFileName = "myTempFile.dat" +const tempDirName = "myTempDir" + +func printPath(path string, err error) string { + if err != nil { + log.Fatal(err) + } + fmt.Println("TempDir created with path: ", path) + return path +} + +func createdFile(path string) { + filepath := filepath.Join(path, tempFileName) + myFile, err := os.Create(filepath) + if err != nil { + log.Fatal(err) + } + defer myFile.Close() + fmt.Println("TempFile created with path: ", filepath) + fmt.Println() + +} + +func main() { + os.Mkdir(tempDirName, 0777) + + path := printPath(os.MkdirTemp(tempDirName, "*")) // случайное имя временного каталога + createdFile(path) + + path = printPath(os.MkdirTemp(tempDirName, "temp*dir")) // имя с использованием шаблона + createdFile(path) + + path = printPath(os.MkdirTemp(tempDirName, "tempdir")) // имя с использованием шаблона + createdFile(path) + // создание временного каталога со случайным именем + // в системной директории для временных файлов + path = printPath(os.MkdirTemp("", "*")) + createdFile(path) +} diff --git a/part_5/5.2/4.go b/part_5/5.2/4.go new file mode 100644 index 0000000..b591fba --- /dev/null +++ b/part_5/5.2/4.go @@ -0,0 +1,62 @@ +package main + +import ( + "fmt" + "log" + "os" + "path/filepath" +) + +const tempFileName = "myTempFile.dat" +const tempDirName = "myTempDir" + +func printPath(path string, err error) string { + if err != nil { + log.Fatal(err) + } + fmt.Println("TempDir created with path: ", path) + return path +} + +func createdFile(path string) { + filepath := filepath.Join(path, tempFileName) + myFile, err := os.Create(filepath) + if err != nil { + log.Fatal(err) + } + defer myFile.Close() + fmt.Println("TempFile created with path: ", filepath) + fmt.Println() + +} +func deleteTempDir(tempDirSlice []string) { + for _, it := range tempDirSlice { + os.RemoveAll(it) + fmt.Println("TempDir with path:", it, "deleted") + } +} + +func main() { + tempDirSlice := []string{} + + os.Mkdir(tempDirName, 0777) + + path := printPath(os.MkdirTemp(tempDirName, "*")) // случайное имя временного каталога + createdFile(path) + tempDirSlice = append(tempDirSlice, path) + + path = printPath(os.MkdirTemp(tempDirName, "temp*dir")) // имя с использованием шаблона + createdFile(path) + tempDirSlice = append(tempDirSlice, path) + + path = printPath(os.MkdirTemp(tempDirName, "tempdir")) // имя с использованием шаблона + createdFile(path) + tempDirSlice = append(tempDirSlice, path) + // создание временного каталога со случайным именем + // в системной директории для временных файлов + path = printPath(os.MkdirTemp("", "*")) + createdFile(path) + tempDirSlice = append(tempDirSlice, path) + + deleteTempDir(tempDirSlice) // удаление всех временных каталогов +} diff --git a/part_5/5.2/5.go b/part_5/5.2/5.go new file mode 100644 index 0000000..0840214 --- /dev/null +++ b/part_5/5.2/5.go @@ -0,0 +1,26 @@ +package main + +import ( + "fmt" + "log" + "os" + "path/filepath" +) + +const tempFileName = "myTempFile.dat" + +func createdFile(path string) { + filepath := filepath.Join(path, tempFileName) + myFile, err := os.Create(filepath) + if err != nil { + log.Fatal(err) + } + defer myFile.Close() + fmt.Println("File created with path: ", filepath) +} + +func main() { + path := filepath.Join("firstDir", "secondDir", "new") + os.MkdirAll(path, 0777) + createdFile(path) +} diff --git a/part_5/5.2/6.go b/part_5/5.2/6.go new file mode 100644 index 0000000..464a404 --- /dev/null +++ b/part_5/5.2/6.go @@ -0,0 +1,26 @@ +package main + +import ( + "fmt" + "log" + "os" + "path/filepath" +) + +func main() { + path := "C:\\Go" // целевая директория + files, err := os.ReadDir(path) // тип возвращаемого значения - []fs.FileInfo + if err != nil { + log.Fatal(err) + } + + for _, file := range files { + if file.IsDir() { + fmt.Printf("%s is directory. Path: %v\n", + file.Name(), filepath.Join(path, file.Name())) + } else { + fmt.Printf("%s is file. Path: %v\n", + file.Name(), filepath.Join(path, file.Name())) + } + } +} diff --git a/part_5/5.2/7.go b/part_5/5.2/7.go new file mode 100644 index 0000000..c218017 --- /dev/null +++ b/part_5/5.2/7.go @@ -0,0 +1,16 @@ +package main + +import ( + "fmt" + "log" + "os" +) + +func main() { + path, err := os.Getwd() + if err != nil { + log.Fatal(err) + } + + fmt.Println(path) // e:\code\golang\directory +} diff --git a/part_5/5.2/8.go b/part_5/5.2/8.go new file mode 100644 index 0000000..ac3aa72 --- /dev/null +++ b/part_5/5.2/8.go @@ -0,0 +1,27 @@ +package main + +import ( + "fmt" + "log" + "os" +) + +func getWD() { + path, err := os.Getwd() + if err != nil { + log.Fatal(err) + } + fmt.Println("Current path:", path) +} + +func main() { + getWD() + os.Mkdir("myDir", 0777) + os.Chdir("./myDir") + getWD() + myFile, err := os.Create("test.dat") + if err != nil { + log.Fatal(err) + } + defer myFile.Close() +} diff --git a/part_5/5.2/9.go b/part_5/5.2/9.go new file mode 100644 index 0000000..69377a5 --- /dev/null +++ b/part_5/5.2/9.go @@ -0,0 +1,21 @@ +package main + +import ( + "fmt" + "log" + "os" +) + +func getWD() { + path, err := os.Getwd() + if err != nil { + log.Fatal(err) + } + fmt.Println("Current path:", path) +} + +func main() { + getWD() + os.Chdir("../") + getWD() +} diff --git a/part_5/5.3/1.go b/part_5/5.3/1.go new file mode 100644 index 0000000..7939dac --- /dev/null +++ b/part_5/5.3/1.go @@ -0,0 +1,30 @@ +package main + +import ( + "encoding/json" + "fmt" + "log" +) + +type Actor struct { + Name string + Age int + FilmsAmount int + AboutActor string +} + +func main() { + actor := Actor{ + Name: "Tom Hanks", + Age: 65, + FilmsAmount: 50, + AboutActor: "Tom Hanks is an actor...", + } + + actorJson, err := json.Marshal(actor) + if err != nil { + log.Fatal(err) + } + + fmt.Println(string(actorJson)) +} diff --git a/part_5/5.3/2.go b/part_5/5.3/2.go new file mode 100644 index 0000000..7677527 --- /dev/null +++ b/part_5/5.3/2.go @@ -0,0 +1,36 @@ +package main + +import ( + "encoding/json" + "log" + "os" +) + +type Actor struct { + Name string + Age int + FilmsAmount int + AboutActor string +} + +func main() { + actor := Actor{ + Name: "Tom Hanks", + Age: 65, + FilmsAmount: 50, + AboutActor: "Tom Hanks is an actor...", + } + + actorJson, err := json.MarshalIndent(actor, "", " ") + if err != nil { + log.Fatal(err) + } + + myFile, err := os.Create("actor.json") + if err != nil { + log.Fatal(err) + } + + myFile.Write(actorJson) + defer myFile.Close() +} diff --git a/part_5/5.3/3.go b/part_5/5.3/3.go new file mode 100644 index 0000000..52ad44e --- /dev/null +++ b/part_5/5.3/3.go @@ -0,0 +1,31 @@ +package main + +import ( + "encoding/json" + "fmt" + "log" + "os" +) + +type Actor struct { + Name string + Age int + FilmsAmount int + AboutActor string +} + +func main() { + actor := &Actor{} + + data, err := os.ReadFile("actor.json") + if err != nil { + log.Fatal(err) + } + + err = json.Unmarshal(data, actor) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("%+v", *actor) +} diff --git a/part_5/5.3/4.go b/part_5/5.3/4.go new file mode 100644 index 0000000..455a303 --- /dev/null +++ b/part_5/5.3/4.go @@ -0,0 +1,43 @@ +package main + +import ( + "encoding/json" + "fmt" + "log" + "os" +) + +type Actor struct { + Name string + Age int + FilmsAmount int + AboutActor *string +} + +func main() { + actor := &Actor{} + + data, err := os.ReadFile("actor.json") + if err != nil { + log.Fatal(err) + } + + err = json.Unmarshal(data, actor) + if err != nil { + log.Fatal(err) + } + + // Выводим данные, прочитанные из файла + fmt.Print("Deserialized data: ") + fmt.Printf("%+v\n", *actor) + + actorJson, err := json.MarshalIndent(actor, "", " ") // сериализация + if err != nil { + log.Fatal(err) + } + + // Выводим сериализованные данные + fmt.Println() + fmt.Println("Serialized data:") + fmt.Println(string(actorJson)) +} diff --git a/part_5/5.3/5.go b/part_5/5.3/5.go new file mode 100644 index 0000000..2bccca6 --- /dev/null +++ b/part_5/5.3/5.go @@ -0,0 +1,33 @@ +package main + +import ( + "encoding/json" + "fmt" + "log" +) + +type Actor struct { + // имя поля при сериализации/десериализации + Name string `json:"name"` + Age int `json:"age"` + // имя тега может отличаться от имени поля структуры + FilmsAmount int `json:"films_amount"` + AboutActor *string `json:"about"` +} + +func main() { + aboutStr := "Tom Hanks is an actor..." + actor := Actor{ + Name: "Tom Hanks", + Age: 65, + FilmsAmount: 50, + AboutActor: &aboutStr, + } + + actorJson, err := json.MarshalIndent(actor, "", " ") + if err != nil { + log.Fatal(err) + } + + fmt.Println(string(actorJson)) +} diff --git a/part_5/5.3/6.go b/part_5/5.3/6.go new file mode 100644 index 0000000..0cfe61e --- /dev/null +++ b/part_5/5.3/6.go @@ -0,0 +1,33 @@ +package main + +import ( + "encoding/json" + "fmt" + "log" +) + +type Actor struct { + // имя поля при сериализации/десериализации + Name string `json:"name"` + Age int `json:"-"` + // имя тега может отличаться от имени поля структуры + FilmsAmount int `json:"-"` + AboutActor *string `json:"about"` +} + +func main() { + aboutStr := "Tom Hanks is an actor..." + actor := Actor{ + Name: "Tom Hanks", + Age: 65, + FilmsAmount: 50, + AboutActor: &aboutStr, + } + + actorJson, err := json.MarshalIndent(actor, "", " ") + if err != nil { + log.Fatal(err) + } + + fmt.Println(string(actorJson)) +} diff --git a/part_5/5.3/7.go b/part_5/5.3/7.go new file mode 100644 index 0000000..e890e6e --- /dev/null +++ b/part_5/5.3/7.go @@ -0,0 +1,54 @@ +package main + +import ( + "encoding/json" + "fmt" + "log" +) + +type Actor struct { + // имя поля при сериализации/десериализации + Name string `json:"name"` + Age int `json:"age"` + // имя тега может отличаться от имени поля структуры + FilmsAmount int `json:"films_amount,omitempty"` + AboutActor *string `json:",omitempty"` +} + +func ActorToBytes(actor Actor) []byte { + actorJson, err := json.MarshalIndent(actor, "", " ") + if err != nil { + log.Fatal(err) + } + return actorJson +} + +func main() { + aboutStr := "Tom Hanks is an actor..." + // поле FilmsAmount проинициализируется значением + // по умолчанию + actor := Actor{ + Name: "Tom Hanks", + Age: 65, + AboutActor: &aboutStr, + } + + actorJson := ActorToBytes(actor) + // Выводим сериализованные данные + fmt.Println() + fmt.Println("First serialized data:") + fmt.Println(string(actorJson)) + + // поле AboutActor и Age проинициализируются значением + // по умолчанию + actor = Actor{ + Name: "Tom Hanks", + FilmsAmount: 150, + } + + actorJson = ActorToBytes(actor) + // Выводим сериализованные данные + fmt.Println() + fmt.Println("Second serialized data:") + fmt.Println(string(actorJson)) +} diff --git a/part_5/5.3/8.go b/part_5/5.3/8.go new file mode 100644 index 0000000..c4e96e2 --- /dev/null +++ b/part_5/5.3/8.go @@ -0,0 +1,32 @@ +package main + +import ( + "encoding/json" + "fmt" + "log" + "os" +) + +type Actor struct { + // имя поля при сериализации/десериализации + Name string `json:"name"` + Age int `json:"age"` + // имя тега может отличаться от имени поля структуры + FilmsAmount int `json:"films_amount,omitempty"` + AboutActor *string `json:",omitempty"` +} + +func main() { + actorJson := map[string]any{} + + data, err := os.ReadFile("actor.json") + if err != nil { + log.Fatal(err) + } + + err = json.Unmarshal(data, &actorJson) + if err != nil { + log.Fatal(err) + } + fmt.Printf("%+v", actorJson) +} diff --git a/part_5/5.3/filmography/go.mod b/part_5/5.3/filmography/go.mod new file mode 100644 index 0000000..49d142a --- /dev/null +++ b/part_5/5.3/filmography/go.mod @@ -0,0 +1,3 @@ +module filmography + +go 1.24.3 diff --git a/part_5/5.3/filmography/imdb/actor.go b/part_5/5.3/filmography/imdb/actor.go new file mode 100644 index 0000000..678d838 --- /dev/null +++ b/part_5/5.3/filmography/imdb/actor.go @@ -0,0 +1,8 @@ +package imdb + +type Actor struct { + Name string `json:"name"` + Age int `json:"age"` + FilmsAmount int `json:"filmsAmount"` + AboutActor string `json:"aboutActor"` +} diff --git a/part_5/5.3/filmography/imdb/genre.go b/part_5/5.3/filmography/imdb/genre.go new file mode 100644 index 0000000..a83ee23 --- /dev/null +++ b/part_5/5.3/filmography/imdb/genre.go @@ -0,0 +1,5 @@ +package imdb + +type Genre struct { + Types []string `json:"types"` +} diff --git a/part_5/5.3/filmography/imdb/movie.go b/part_5/5.3/filmography/imdb/movie.go new file mode 100644 index 0000000..a435ea7 --- /dev/null +++ b/part_5/5.3/filmography/imdb/movie.go @@ -0,0 +1,13 @@ +package imdb + +type Movie struct { + Name string `json:"name"` + Budget int `json:"budget"` + Actors []Actor `json:"actors"` + CriticsRating float64 `json:"criticsRating"` + AudienceRating float64 `json:"audienceRating"` + Year int `json:"year"` + Country string `json:"country"` + Genre Genre `json:"genre"` + Reviews []Review `json:"reviews"` +} diff --git a/part_5/5.3/filmography/imdb/review.go b/part_5/5.3/filmography/imdb/review.go new file mode 100644 index 0000000..e6b7ad8 --- /dev/null +++ b/part_5/5.3/filmography/imdb/review.go @@ -0,0 +1,7 @@ +package imdb + +type Review struct { + Name string `json:"name"` + Text string `json:"text"` + Rating int `json:"rating"` +} diff --git a/part_5/5.3/filmography/main.go b/part_5/5.3/filmography/main.go new file mode 100644 index 0000000..f5ad9d2 --- /dev/null +++ b/part_5/5.3/filmography/main.go @@ -0,0 +1,33 @@ +package main + +import ( + "encoding/json" + "filmography/imdb" + "fmt" + "log" + "os" +) + +func main() { + movie := &imdb.Movie{} + data, err := os.ReadFile("movie.json") + if err != nil { + log.Fatal(err) + } + + err = json.Unmarshal(data, &movie) + if err != nil { + log.Fatal(err) + } + fmt.Printf("%+v", movie) + + // записать в файл output.json + data, err = json.MarshalIndent(movie, "", " ") + if err != nil { + log.Fatal(err) + } + err = os.WriteFile("output.json", data, 0644) + if err != nil { + log.Fatal(err) + } +} diff --git a/part_5/5.3/filmography/movie.json b/part_5/5.3/filmography/movie.json new file mode 100644 index 0000000..24b5c26 --- /dev/null +++ b/part_5/5.3/filmography/movie.json @@ -0,0 +1,52 @@ +{ + "name": "Prevozmogun", + "budget": 2000000, + "actors": [ + { + "name": "Alexey", + "age": 25, + "filmsAmount": 3, + "aboutActor": "2 academy awards" + }, + { + "name": "Max", + "age": 33, + "filmsAmount": 2, + "aboutActor": "Very good actor" + }, + { + "name": "Natalya", + "age": 18, + "filmsAmount": 1, + "aboutActor": "Rising star" + } + ], + "criticsRating": 7.5, + "audienceRating": 8.5, + "year": 2019, + "country": "Russia", + "genre": { + "types": [ + "Comedy", + "Drama", + "Romance" + ] + }, + "reviews": [ + { + "name": "Igor", + "text": "I love this movie", + "rating": 10 + }, + { + "name": "John", + "text": "I hate this movie", + "rating": 1 + }, + { + "name": "Stas", + "text": "I like this movie", + "rating": 7 + } + ] +} \ No newline at end of file diff --git a/part_5/5.3/filmography/output.json b/part_5/5.3/filmography/output.json new file mode 100644 index 0000000..24d23b6 --- /dev/null +++ b/part_5/5.3/filmography/output.json @@ -0,0 +1,52 @@ +{ + "name": "Prevozmogun", + "budget": 2000000, + "actors": [ + { + "name": "Alexey", + "age": 25, + "filmsAmount": 3, + "aboutActor": "2 academy awards" + }, + { + "name": "Max", + "age": 33, + "filmsAmount": 2, + "aboutActor": "Very good actor" + }, + { + "name": "Natalya", + "age": 18, + "filmsAmount": 1, + "aboutActor": "Rising star" + } + ], + "criticsRating": 7.5, + "audienceRating": 8.5, + "year": 2019, + "country": "Russia", + "genre": { + "types": [ + "Comedy", + "Drama", + "Romance" + ] + }, + "reviews": [ + { + "name": "Igor", + "text": "I love this movie", + "rating": 10 + }, + { + "name": "John", + "text": "I hate this movie", + "rating": 1 + }, + { + "name": "Stas", + "text": "I like this movie", + "rating": 7 + } + ] +} \ No newline at end of file diff --git a/part_5/5.6/go_dotenv/dotenv/dotenv.go b/part_5/5.6/go_dotenv/dotenv/dotenv.go new file mode 100644 index 0000000..8e580e2 --- /dev/null +++ b/part_5/5.6/go_dotenv/dotenv/dotenv.go @@ -0,0 +1,162 @@ +package dotenv + +import ( + "bufio" + "errors" + "fmt" + "maps" + "os" + "regexp" + "strconv" + "strings" +) + +type DotEnv struct { + env map[string]interface{} +} + +// Регулярные выражения для int и bool +var ( + intPattern = regexp.MustCompile(`^-?\d+$`) + boolPattern = regexp.MustCompile(`^(?i:true|false)$`) +) + +// Создаем новый экземпляр DotEnv +// platformEnv указывает, загружать ли переменные из окружения ОС +func NewDotEnv(platformEnv bool) *DotEnv { + d := &DotEnv{env: make(map[string]interface{})} + if platformEnv { + d.env = mergeMaps(d.env, loadPlatformEnv()) + } + return d +} + +// Загружаем переменные из .env файлов +func (d *DotEnv) Load(paths ...string) error { + if len(paths) == 0 { + paths = []string{".env"} + } + for _, path := range paths { + file, err := os.Open(path) + if err != nil { + return fmt.Errorf(".env file not found: %s", path) + } + defer file.Close() + + localEnv, err := loadLocalEnv(file) + if err != nil { + return err + } + d.env = mergeMaps(d.env, localEnv) + } + return nil +} + +// Возвращает значение переменной окружения по ключу +func (d *DotEnv) GetValue(key string) (interface{}, error) { + val, ok := d.env[key] + if !ok { + return nil, fmt.Errorf("key not found: %s", key) + } + return val, nil +} + +// Возвращает значение переменной окружения по ключу, +// приведенное к int. Если значение не является int, +// возвращается ошибка. +func (d *DotEnv) GetInt(key string) (int, error) { + val, ok := d.env[key] + if !ok { + return 0, fmt.Errorf("key not found: %s", key) + } + i, ok := val.(int) + if !ok { + return 0, fmt.Errorf("value for key %s is not an int", key) + } + return i, nil +} + +// Возвращает значение переменной окружения по ключу, +// приведенное к bool. Если значение не является bool, +// возвращается ошибка. +func (d *DotEnv) GetBool(key string) (bool, error) { + val, ok := d.env[key] + if !ok { + return false, fmt.Errorf("key not found: %s", key) + } + b, ok := val.(bool) + if !ok { + return false, fmt.Errorf("value for key %s is not a bool", key) + } + return b, nil +} + +// Возвращает значение переменной окружения по ключу, +// приведенное к string. +func (d *DotEnv) GetString(key string) (string, error) { + val, ok := d.env[key] + if !ok { + return "", fmt.Errorf("key not found: %s", key) + } + s, ok := val.(string) + if !ok { + return "", fmt.Errorf("value for key %s is not a string", key) + } + return s, nil +} + +// Преобразование таблицы из переменных окружения ОС +func loadPlatformEnv() map[string]interface{} { + config := make(map[string]interface{}) + for _, value := range os.Environ() { + parts := strings.SplitN(value, "=", 2) + if len(parts) != 2 { + continue + } + k := parts[0] + v := parts[1] + + config[k] = parseValue(v) + } + return config +} + +// Загрузка из локального .env файла +func loadLocalEnv(file *os.File) (map[string]interface{}, error) { + scanner := bufio.NewScanner(file) + config := make(map[string]interface{}) + + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if line == "" || strings.HasPrefix(line, "#") { + continue + } + parts := strings.SplitN(line, "=", 2) + if len(parts) != 2 { + return nil, errors.New("invalid line in .env file: " + line) + } + key := strings.TrimSpace(parts[0]) + val := strings.TrimSpace(parts[1]) + config[key] = parseValue(val) + } + return config, nil +} + +// Парсинг строки в int, bool или string +func parseValue(val string) interface{} { + if intPattern.MatchString(val) { + if i, err := strconv.Atoi(val); err == nil { + return i + } + } + if boolPattern.MatchString(val) { + return strings.ToLower(val) == "true" + } + return val +} + +// Объединение двух map +func mergeMaps(a, b map[string]interface{}) map[string]interface{} { + maps.Copy(a, b) + return a +} diff --git a/part_5/5.6/go_dotenv/go.mod b/part_5/5.6/go_dotenv/go.mod new file mode 100644 index 0000000..a0f3fb6 --- /dev/null +++ b/part_5/5.6/go_dotenv/go.mod @@ -0,0 +1,3 @@ +module go_dotenv + +go 1.24.0 diff --git a/part_5/5.6/go_dotenv/main.go b/part_5/5.6/go_dotenv/main.go new file mode 100644 index 0000000..6294737 --- /dev/null +++ b/part_5/5.6/go_dotenv/main.go @@ -0,0 +1,40 @@ +package main + +import ( + "fmt" + "go_dotenv/dotenv" + "log" +) + +func main() { + env := dotenv.NewDotEnv(false) + + err := env.Load(".env") + if err != nil { + log.Fatal(err) + } + + adminID, err := env.GetInt("ADMIN_ID") + if err != nil { + log.Fatal(err) + } + fmt.Println(adminID) + + adminPassword, err := env.GetString("ADMIN_PASSWORD") + if err != nil { + log.Fatal(err) + } + fmt.Println(adminPassword) + + adminMode, err := env.GetBool("ADMIN_MODE") + if err != nil { + log.Fatal(err) + } + fmt.Println(adminMode) + + adminEmail, err := env.GetString("ADMIN_EMAIL") + if err != nil { + log.Fatal(err) + } + fmt.Println(adminEmail) +} diff --git a/part_5/5.6/my_app/go.mod b/part_5/5.6/my_app/go.mod new file mode 100644 index 0000000..ae0f2c8 --- /dev/null +++ b/part_5/5.6/my_app/go.mod @@ -0,0 +1,3 @@ +module my_app + +go 1.24.0 diff --git a/part_5/5.6/my_app/main.go b/part_5/5.6/my_app/main.go new file mode 100644 index 0000000..d7b26ac --- /dev/null +++ b/part_5/5.6/my_app/main.go @@ -0,0 +1,24 @@ +package main + +import ( + "flag" + "fmt" +) + +func main() { + // конфигурируем флаги: + // Первый аргумент - название флага + // Второй - значение по умолчанию + // Третий - описание того, что делает флаг + myStr := flag.String("str", "value", "StrFlag description") + myInt := flag.Int("int", 0, "IntFlag description") + myBool := flag.Bool("bool", false, "BoolFlag description") + + // Запускаем парсинг флагов, подаваемых в командной строке + flag.Parse() + + // используем флаги + fmt.Println("String flag:", *myStr) + fmt.Println("Int flag:", *myInt) + fmt.Println("Bool flag:", *myBool) +} diff --git a/part_5/5.6/my_compile_app/go.mod b/part_5/5.6/my_compile_app/go.mod new file mode 100644 index 0000000..7b9e614 --- /dev/null +++ b/part_5/5.6/my_compile_app/go.mod @@ -0,0 +1,3 @@ +module my_compile_app + +go 1.24.0 diff --git a/part_5/5.6/my_compile_app/main.go b/part_5/5.6/my_compile_app/main.go new file mode 100644 index 0000000..c0435f7 --- /dev/null +++ b/part_5/5.6/my_compile_app/main.go @@ -0,0 +1,12 @@ +package main + +import ( + "fmt" +) + +var VersionType = "dev" +var Version = "0.0.1" + +func main() { + fmt.Printf("Version: %s (%s)\n", Version, VersionType) +} diff --git a/part_5/5.6/my_embed_app/embed_config.json b/part_5/5.6/my_embed_app/embed_config.json new file mode 100644 index 0000000..84c3aca --- /dev/null +++ b/part_5/5.6/my_embed_app/embed_config.json @@ -0,0 +1,6 @@ +{ + "debug": true, + "offset": 10, + "version": "0.0.1", + "app_name": "StaskoGo" +} diff --git a/part_5/5.6/my_embed_app/go.mod b/part_5/5.6/my_embed_app/go.mod new file mode 100644 index 0000000..687b6c5 --- /dev/null +++ b/part_5/5.6/my_embed_app/go.mod @@ -0,0 +1,3 @@ +module my_embed_app + +go 1.24.0 diff --git a/part_5/5.6/my_embed_app/main.go b/part_5/5.6/my_embed_app/main.go new file mode 100644 index 0000000..29a37ab --- /dev/null +++ b/part_5/5.6/my_embed_app/main.go @@ -0,0 +1,32 @@ +package main + +import ( + _ "embed" + "encoding/json" + "fmt" + "log" +) + +//go:embed embed_config.json +var configData []byte + +type Config struct { + Debug bool `json:"debug"` + Offset int `json:"offset"` + AppName string `json:"app_name"` + Version string `json:"version"` +} + +func getConfig() Config { + var config Config + err := json.Unmarshal(configData, &config) + if err != nil { + log.Fatal(err) + } + return config +} + +func main() { + config := getConfig() + fmt.Println(config) +} diff --git a/part_5/go_database/.vscode/launch.json b/part_5/go_database/.vscode/launch.json new file mode 100644 index 0000000..c147665 --- /dev/null +++ b/part_5/go_database/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + "version": "0.2.0", + "configurations": [ + + { + "name": "Launch Package", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${fileDirname}", + "console": "integratedTerminal" + } + ] +} diff --git a/part_5/go_database/database/database.go b/part_5/go_database/database/database.go new file mode 100644 index 0000000..be61653 --- /dev/null +++ b/part_5/go_database/database/database.go @@ -0,0 +1,218 @@ +package database + +import ( + "bufio" + "fmt" + "os" + "path/filepath" + "strconv" + "strings" +) + +type DBType int + +const ( + SUAI DBType = iota + UNECON +) + +type Database struct { + PathToSuaiDB string + PathToUneconDB string + SuaiUsers *Table[*User] + UneconUsers *Table[*User] +} + +func NewDatabase(pathToSuaiDB, pathToUneconDB string) *Database { + db := &Database{ + PathToSuaiDB: pathToSuaiDB, + PathToUneconDB: pathToUneconDB, + } + + if _, err := os.Stat(pathToSuaiDB); os.IsNotExist(err) { + dir := filepath.Dir(pathToSuaiDB) + if err := os.MkdirAll(dir, 0755); err != nil { + fmt.Println("Error creating directory:", err) + } + + file, err := os.Create(pathToSuaiDB) + if err != nil { + fmt.Println("Error creating file:", err) + } + file.Close() + db.SuaiUsers = NewTable[*User]("suai") + } else { + db.SuaiUsers = db.openTable(pathToSuaiDB, "suai") + } + + if _, err := os.Stat(pathToUneconDB); os.IsNotExist(err) { + dir := filepath.Dir(pathToUneconDB) + if err := os.MkdirAll(dir, 0755); err != nil { + fmt.Println("Error creating directory:", err) + } + + file, err := os.Create(pathToUneconDB) + if err != nil { + fmt.Println("Error creating file:", err) + } + file.Close() + db.UneconUsers = NewTable[*User]("unecon") + } else { + db.UneconUsers = db.openTable(pathToUneconDB, "unecon") + } + + return db +} + +func (db *Database) openTable(filePath, tableName string) *Table[*User] { + table := NewTable[*User](tableName) + + file, err := os.Open(filePath) + if err != nil { + fmt.Println("Error opening file:", err) + return table + } + defer file.Close() + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + data := strings.Split(line, ",") + if len(data) >= 7 { + id, _ := strconv.Atoi(data[0]) + yearOfBirth, _ := strconv.Atoi(data[2]) + + user := NewUser( + id, + data[1], + yearOfBirth, + data[3], + data[4], + data[5], + data[6], + ) + + table.Insert(user) + } + } + + return table +} + +func (db *Database) Save(dbType DBType) { + var filePath string + var users *Table[*User] + + switch dbType { + case SUAI: + filePath = db.PathToSuaiDB + users = db.SuaiUsers + case UNECON: + filePath = db.PathToUneconDB + users = db.UneconUsers + } + + file, err := os.Create(filePath) + if err != nil { + fmt.Println("Error opening file for writing:", err) + return + } + defer file.Close() + + writer := bufio.NewWriter(file) + users.ForEach(func(user *User) { + line := fmt.Sprintf("%d,%s,%d,%s,%s,%s,%s\n", + user.ID(), + user.Nickname(), + user.YearOfBirth(), + user.Email(), + user.Phone(), + user.AccessLevel(), + user.PasswordHash(), + ) + writer.WriteString(line) + }) + writer.Flush() +} + +func (db *Database) Insert(user *User, dbType DBType) bool { + var filePath string + var isOk bool + + switch dbType { + case SUAI: + filePath = db.PathToSuaiDB + isOk = db.SuaiUsers.Insert(user) + case UNECON: + filePath = db.PathToUneconDB + isOk = db.UneconUsers.Insert(user) + } + + if !isOk { + return false + } + + file, err := os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY, 0644) + if err != nil { + fmt.Println("Error opening file for appending:", err) + return false + } + defer file.Close() + + line := fmt.Sprintf("%d,%s,%d,%s,%s,%s,%s\n", + user.ID(), + user.Nickname(), + user.YearOfBirth(), + user.Email(), + user.Phone(), + user.AccessLevel(), + user.PasswordHash(), + ) + + if _, err := file.WriteString(line); err != nil { + fmt.Println("Error writing to file:", err) + return false + } + + return true +} + +func (db *Database) Selection(dbType DBType, + attribute, value string) *Table[*User] { + switch dbType { + case SUAI: + return db.SuaiUsers.Selection(attribute, value) + case UNECON: + return db.UneconUsers.Selection(attribute, value) + default: + return NewTable[*User]("empty") + } +} + +func (db *Database) Intersect(attribute, value string) *Table[*User] { + return db.SuaiUsers.Intersect(attribute, value, db.UneconUsers) +} + +func (db *Database) Union() *Table[*User] { + return db.SuaiUsers.Union(db.UneconUsers) +} + +func (db *Database) Remove(id string, dbType DBType) { + switch dbType { + case SUAI: + db.SuaiUsers.Remove(id) + fmt.Println(db.SuaiUsers) + case UNECON: + db.UneconUsers.Remove(id) + fmt.Println(db.UneconUsers) + } +} + +func (db *Database) ShowDB(dbType DBType) { + switch dbType { + case SUAI: + fmt.Println(db.SuaiUsers) + case UNECON: + fmt.Println(db.UneconUsers) + } +} diff --git a/part_5/go_database/database/i_attribute.go b/part_5/go_database/database/i_attribute.go new file mode 100644 index 0000000..fea218c --- /dev/null +++ b/part_5/go_database/database/i_attribute.go @@ -0,0 +1,8 @@ +package database + +type IAttribute interface { + Check(attribute, value string) bool + Change(attribute, value string) bool + + String() string +} diff --git a/part_5/go_database/database/table.go b/part_5/go_database/database/table.go new file mode 100644 index 0000000..23f9bfa --- /dev/null +++ b/part_5/go_database/database/table.go @@ -0,0 +1,155 @@ +package database + +import ( + "fmt" + "strconv" + "strings" +) + +type Node[T IAttribute] struct { + Data T + Next *Node[T] +} + +type Table[T IAttribute] struct { + head *Node[T] + tail *Node[T] + Title string +} + +func NewTable[T IAttribute](title string) *Table[T] { + return &Table[T]{Title: title} +} + +func (t *Table[T]) First() T { + if t.head != nil { + return t.head.Data + } + var zero T + return zero +} + +func (t *Table[T]) Last() T { + if t.tail != nil { + return t.tail.Data + } + var zero T + return zero +} + +func (t *Table[T]) Contains(data T) bool { + user, ok := any(data).(*User) + if !ok { + return false + } + + temp := t.head + for temp != nil { + if temp.Data.Check(FieldID, strconv.Itoa(user.ID())) { + return true + } + temp = temp.Next + } + return false +} + +func (t *Table[T]) Insert(data T) bool { + if t.Contains(data) { + return false + } + node := &Node[T]{Data: data} + if t.head == nil { + t.head = node + t.tail = node + } else { + t.tail.Next = node + t.tail = node + } + return true +} + +func (t *Table[T]) Remove(id string) { + var prev *Node[T] + curr := t.head + + for curr != nil { + if curr.Data.Check("id", id) { + if prev == nil { + t.head = curr.Next + if t.head == nil { + t.tail = nil + } + } else { + prev.Next = curr.Next + if prev.Next == nil { + t.tail = prev + } + } + return + } + prev = curr + curr = curr.Next + } +} + +func (t *Table[T]) Intersect(attribute, value string, + other *Table[T]) *Table[T] { + + newTable := NewTable[T](fmt.Sprintf("%s-%s", t.Title, other.Title)) + + for temp := t.head; temp != nil; temp = temp.Next { + if temp.Data.Check(attribute, value) { + newTable.Insert(temp.Data) + } + } + for temp := other.head; temp != nil; temp = temp.Next { + if temp.Data.Check(attribute, value) && + !newTable.Contains(temp.Data) { + newTable.Insert(temp.Data) + } + } + return newTable +} + +func (t *Table[T]) Union(other *Table[T]) *Table[T] { + newTable := NewTable[T](fmt.Sprintf("%s-%s", t.Title, other.Title)) + + for temp := t.head; temp != nil; temp = temp.Next { + newTable.Insert(temp.Data) + } + for temp := other.head; temp != nil; temp = temp.Next { + if !newTable.Contains(temp.Data) { + newTable.Insert(temp.Data) + } + } + return newTable +} + +func (t *Table[T]) Selection(attribute, value string) *Table[T] { + newTable := NewTable[T](t.Title + "-new") + + for temp := t.head; temp != nil; temp = temp.Next { + if temp.Data.Check(attribute, value) { + newTable.Insert(temp.Data) + } + } + return newTable +} + +func (t *Table[T]) ForEach(action func(T)) { + for temp := t.head; temp != nil; temp = temp.Next { + action(temp.Data) + } +} + +func (t *Table[T]) String() string { + var sb strings.Builder + sb.WriteString(fmt.Sprintf("%s%s%s\n", strings.Repeat("*", 10), + t.Title, strings.Repeat("*", 10))) + + for temp := t.head; temp != nil; temp = temp.Next { + sb.WriteString(fmt.Sprintf("%s\n", temp.Data.String())) + } + + return sb.String() +} diff --git a/part_5/go_database/database/user.go b/part_5/go_database/database/user.go new file mode 100644 index 0000000..9c17ddc --- /dev/null +++ b/part_5/go_database/database/user.go @@ -0,0 +1,132 @@ +package database + +import ( + "fmt" + "strconv" + "strings" +) + +type AccessLevel = string + +const ( + Student AccessLevel = "S" + Teacher AccessLevel = "T" + Admin AccessLevel = "A" +) + +func AcsessLevelFromString(s string) AccessLevel { + switch strings.ToUpper(s) { + case "T": + return Teacher + case "A": + return Admin + default: + return Student + } +} + +type UserTableField = string + +const ( + FieldID UserTableField = "id" + FieldNickname UserTableField = "nickname" + FieldYearOfBirth UserTableField = "yearOfBirth" + FieldEmail UserTableField = "email" + FieldPhone UserTableField = "phone" + FieldAccessLevel UserTableField = "accessLevel" + FieldPasswordHash UserTableField = "passwordHash" +) + +type User struct { + id int + nickname string + yearOfBirth int + email string + phone string + accessLevel AccessLevel + passwordHash string +} + +func NewUser( + id int, + nickname string, + yearOfBirth int, + email string, + phone string, + accessLevel string, + passwordHash string, +) *User { + return &User{ + id: id, + nickname: nickname, + yearOfBirth: yearOfBirth, + email: email, + phone: phone, + accessLevel: AcsessLevelFromString(accessLevel), + passwordHash: passwordHash, + } +} + +func (u *User) ID() int { return u.id } +func (u *User) Nickname() string { return u.nickname } +func (u *User) YearOfBirth() int { return u.yearOfBirth } +func (u *User) Email() string { return u.email } +func (u *User) Phone() string { return u.phone } +func (u *User) AccessLevel() string { return u.accessLevel } +func (u *User) PasswordHash() string { return u.passwordHash } + +func (u *User) Change(attr, value string) bool { + switch attr { + case FieldNickname: + u.nickname = value + case FieldYearOfBirth: + v, err := strconv.Atoi(value) + if err != nil { + return false + } + u.yearOfBirth = v + case FieldEmail: + u.email = value + case FieldPhone: + u.phone = value + case FieldAccessLevel: + u.accessLevel = AcsessLevelFromString(value) + case FieldPasswordHash: + u.passwordHash = value + default: + return false + } + return true +} + +func (u *User) Check(attr, value string) bool { + switch attr { + case FieldID: + v, err := strconv.Atoi(value) + return err == nil && u.id == v + case FieldNickname: + return u.nickname == value + case FieldYearOfBirth: + v, err := strconv.Atoi(value) + return err == nil && u.yearOfBirth == v + case FieldEmail: + return u.email == value + case FieldPhone: + return u.phone == value + case FieldAccessLevel: + return u.accessLevel == value + case FieldPasswordHash: + return u.passwordHash == value + default: + return false + } +} + +func (u *User) String() string { + return fmt.Sprintf( + "User(id: %d, nickname: %s, yearOfBirth: %d, "+ + "email: %s, phone: %s, acsessLevel: %s, passwordHash: %s)", + u.id, u.nickname, u.yearOfBirth, u.email, + u.phone, u.accessLevel, u.passwordHash, + ) +} diff --git a/part_5/go_database/go.mod b/part_5/go_database/go.mod new file mode 100644 index 0000000..a529276 --- /dev/null +++ b/part_5/go_database/go.mod @@ -0,0 +1,3 @@ +module go_database + +go 1.24 diff --git a/part_5/go_database/main.go b/part_5/go_database/main.go new file mode 100644 index 0000000..90e7c4b --- /dev/null +++ b/part_5/go_database/main.go @@ -0,0 +1,16 @@ +package main + +import ( + db "go_database/database" + "path/filepath" +) + +func main() { + db := db.NewDatabase( + filepath.Join(".", "suai.txt"), + filepath.Join(".", "unecon.txt"), + ) + + menu := NewMenu(db) + menu.Loop() +} diff --git a/part_5/go_database/menu.go b/part_5/go_database/menu.go new file mode 100644 index 0000000..d4f6da6 --- /dev/null +++ b/part_5/go_database/menu.go @@ -0,0 +1,214 @@ +package main + +import ( + "bufio" + "fmt" + db "go_database/database" + "os" + "strconv" + "strings" +) + +type Menu struct { + database *db.Database + reader *bufio.Reader +} + +func NewMenu(db *db.Database) *Menu { + return &Menu{ + database: db, + reader: bufio.NewReader(os.Stdin), + } +} + +func (m *Menu) Loop() { + for { + m.PrintMenu() + input, _ := m.reader.ReadString('\n') + input = strings.TrimSpace(input) + + fmt.Println(strings.Repeat("x", 25)) + + switch input { + case "1": + m.AddUser() + case "2": + m.RemoveUser() + case "3": + m.ChangeUser() + case "4": + m.ShowUsers() + case "5": + m.Intersect() + case "6": + m.Union() + case "7": + m.SaveAndExit() + return + case "8": + return + } + } +} + +func (m *Menu) PrintMenu() { + fmt.Println("1. Add User") + fmt.Println("2. Remove User") + fmt.Println("3. Change User") + fmt.Println("4. Show Users") + fmt.Println("5. Intersect Table") + fmt.Println("6. Union Table") + fmt.Println("7. Save and Exit") + fmt.Println("8. Exit") +} + +func (m *Menu) readInput(prompt string) string { + fmt.Print(prompt) + input, _ := m.reader.ReadString('\n') + return strings.TrimSpace(input) +} + +func (m *Menu) AddUser() { + try := func() bool { + idStr := m.readInput("Enter id: ") + id, err := strconv.Atoi(idStr) + if err != nil { + fmt.Println("Invalid ID format") + return false + } + + nickname := m.readInput("Enter nickname: ") + + yearStr := m.readInput("Enter year of birth: ") + yearOfBirth, err := strconv.Atoi(yearStr) + if err != nil { + fmt.Println("Invalid year format") + return false + } + + email := m.readInput("Enter email: ") + phone := m.readInput("Enter phone: ") + passwordHash := m.readInput("Enter password hash: ") + accessLevel := m.readInput("Access level (A - Admin, " + + "T - Teacher, S - Student): ") + + user := db.NewUser( + id, + nickname, + yearOfBirth, + email, + phone, + accessLevel, + passwordHash, + ) + + dbTypeStr := m.readInput("Add user to DB (S - SUAI, U - Unecon): ") + dbTypeStr = strings.ToUpper(dbTypeStr) + + switch dbTypeStr { + case "S": + m.database.Insert(user, db.SUAI) + m.database.ShowDB(db.SUAI) + case "U": + m.database.Insert(user, db.UNECON) + m.database.ShowDB(db.UNECON) + default: + fmt.Println("(ノ-_-)ノ ミ ┴┴") + return false + } + + return true + } + + if !try() { + fmt.Println("WTF!!!!") + } +} + +func (m *Menu) RemoveUser() { + dbTypeStr := m.readInput("Select DB (S - SUAI, U - Unecon): ") + dbTypeStr = strings.ToUpper(dbTypeStr) + + var dbType db.DBType + + switch dbTypeStr { + case "S": + dbType = db.SUAI + m.database.ShowDB(db.SUAI) + case "U": + dbType = db.UNECON + m.database.ShowDB(db.UNECON) + default: + fmt.Println("(ノ-_-)ノ ミ ┴┴") + return + } + + id := m.readInput("Enter id: ") + m.database.Remove(id, dbType) + m.database.ShowDB(dbType) +} + +func (m *Menu) ChangeUser() { + dbTypeStr := m.readInput("Select DB (S - SUAI, U - Unecon): ") + dbTypeStr = strings.ToUpper(dbTypeStr) + + var dbType db.DBType + + switch dbTypeStr { + case "S": + dbType = db.SUAI + m.database.ShowDB(db.SUAI) + case "U": + dbType = db.UNECON + m.database.ShowDB(db.UNECON) + default: + fmt.Println("(ノ-_-)ノ ミ ┴┴") + return + } + + id := m.readInput("Enter id: ") + + userTable := m.database.Selection(dbType, "id", id) + user := userTable.First() + if user == nil { + fmt.Println("User not found!") + return + } + + field := m.readInput("Enter field: ") + newValue := m.readInput("Enter new value: ") + user.Change(field, newValue) + + m.database.ShowDB(dbType) +} + +func (m *Menu) ShowUsers() { + dbTypeStr := m.readInput("Select DB (S - SUAI, U - Unecon): ") + dbTypeStr = strings.ToUpper(dbTypeStr) + + switch dbTypeStr { + case "S": + m.database.ShowDB(db.SUAI) + case "U": + m.database.ShowDB(db.UNECON) + default: + fmt.Println("(ノ-_-)ノ ミ ┴┴") + } +} + +func (m *Menu) Intersect() { + field := m.readInput("Enter field: ") + value := m.readInput("Enter intersect value: ") + result := m.database.Intersect(field, value) + fmt.Println(result) +} + +func (m *Menu) Union() { + result := m.database.Union() + fmt.Println(result) +} + +func (m *Menu) SaveAndExit() { + m.database.Save(db.SUAI) + m.database.Save(db.UNECON) +} diff --git a/part_5/go_database/suai.txt b/part_5/go_database/suai.txt new file mode 100644 index 0000000..abf3301 --- /dev/null +++ b/part_5/go_database/suai.txt @@ -0,0 +1,5 @@ +0,MADTeacher,1989,stasko@gmail.ru,+7xxxxxxx,T,adFF +1,Alex,1990,al@gmail.ru,+7x2x5xxx,A,adadFFxzxc +2,Max,2005,mmm@gmail.ru,+7xx654xx,S,20AFBB +3,Jack,2006,jj@gmail.ru,+7xx657xx,S,41FDF +5,FF,1999,mad@tr.ri,+7xx654xx,S,Hdndgu2397123 diff --git a/part_5/go_database/unecon.txt b/part_5/go_database/unecon.txt new file mode 100644 index 0000000..d2ac2d4 --- /dev/null +++ b/part_5/go_database/unecon.txt @@ -0,0 +1,4 @@ +0,Jack,2007,gf@gmail.ru,+7xx624xx,S,20AAAB +1,MADTeacher,1989,stasko@gmail.ru,+7xxxxxxx,T,adFF +2,Stas,1994,st@gmail.ru,+7x2x2xxx,A,adaFBB +6,Max,2009,mv@gmail.ru,+7xx657xx,S,41FDF diff --git a/part_5/go_json_store/go.mod b/part_5/go_json_store/go.mod new file mode 100644 index 0000000..0de6696 --- /dev/null +++ b/part_5/go_json_store/go.mod @@ -0,0 +1,3 @@ +module go_json_store + +go 1.20 diff --git a/part_5/go_json_store/jsonstore/json_file.go b/part_5/go_json_store/jsonstore/json_file.go new file mode 100644 index 0000000..13d064c --- /dev/null +++ b/part_5/go_json_store/jsonstore/json_file.go @@ -0,0 +1,62 @@ +package jsonstore + +import ( + "encoding/json" + "io" + "os" + "path/filepath" +) + +type JSONFile struct { + Path string +} + +func NewJSONFile(path string) *JSONFile { + dir := filepath.Dir(path) + if _, err := os.Stat(dir); os.IsNotExist(err) { + os.MkdirAll(dir, 0755) + } + return &JSONFile{Path: path} +} + +func (j *JSONFile) Read() map[string]any { + file, err := os.Open(j.Path) + if err != nil { + if os.IsNotExist(err) { + return make(map[string]any) + } + return make(map[string]any) + } + defer file.Close() + + data, err := io.ReadAll(file) + if err != nil { + return make(map[string]any) + } + + if len(data) == 0 { + return make(map[string]any) + } + + var result map[string]any + err = json.Unmarshal(data, &result) + if err != nil { + return make(map[string]any) + } + + return result +} + +func (j *JSONFile) Write(data map[string]any) error { + dir := filepath.Dir(j.Path) + if _, err := os.Stat(dir); os.IsNotExist(err) { + os.MkdirAll(dir, 0755) + } + + jsonData, err := json.MarshalIndent(data, "", " ") + if err != nil { + return err + } + + return os.WriteFile(j.Path, jsonData, 0644) +} diff --git a/part_5/go_json_store/jsonstore/json_store.go b/part_5/go_json_store/jsonstore/json_store.go new file mode 100644 index 0000000..917e248 --- /dev/null +++ b/part_5/go_json_store/jsonstore/json_store.go @@ -0,0 +1,200 @@ +package jsonstore + +import ( + "encoding/json" + "os" + "reflect" +) + +type JSONStore struct { + filePath string + values map[string]interface{} +} + +func NewJSONStore(path string) *JSONStore { + return &JSONStore{ + filePath: path, + } +} + +func (js *JSONStore) load() map[string]interface{} { + if js.values != nil { + return js.values + } + + content, err := os.ReadFile(js.filePath) + if err != nil { + js.values = make(map[string]interface{}) + return js.values + } + + var data map[string]interface{} + if err := json.Unmarshal(content, &data); err != nil { + js.values = make(map[string]interface{}) + return js.values + } + js.values = data + return js.values +} + +func (js *JSONStore) Contains(key string) bool { + _, ok := js.load()[key] + return ok +} + +func (js *JSONStore) Keys() []string { + keys := make([]string, 0) + for k := range js.load() { + keys = append(keys, k) + } + return keys +} + +func (js *JSONStore) Values() []interface{} { + values := make([]interface{}, 0) + for _, v := range js.load() { + values = append(values, v) + } + return values +} + +func (js *JSONStore) GetValue(key string) interface{} { + return js.load()[key] +} + +func (js *JSONStore) GetBool(key string) *bool { + if v, ok := js.GetValue(key).(bool); ok { + return &v + } + return nil +} + +func (js *JSONStore) GetInt(key string) *int { + // Т.к. все числа по умолчанию декодируются как float64, + // то сначала приводим к float64 + if f, ok := js.GetValue(key).(float64); ok { + i := int(f) + return &i + } + return nil +} + +func (js *JSONStore) GetFloat(key string) *float64 { + if v, ok := js.GetValue(key).(float64); ok { + return &v + } + return nil +} + +func (js *JSONStore) GetString(key string) *string { + if v, ok := js.GetValue(key).(string); ok { + return &v + } + return nil +} + +// Метод возвращает по ключу срез []interface{}. +// Если значение хранится как []interface{}, возвращаем его напрямую. +// Если значение хранится как []string, []int или []float64, +// преобразуем каждый элемент к interface{} для совместимости +// с Go JSON API. Это обеспечивает универсальный доступ к массивам +// любого типа, сохраненным в JSON. +func (js *JSONStore) GetList(key string) []interface{} { + // Получаем значение по ключу + val := js.GetValue(key) + // Если значение отсутствует, возвращаем nil + if val == nil { + return nil + } + // Если это уже []interface{} (тип, который возвращает + // json.Unmarshal), возвращаем напрямую + if v, ok := val.([]interface{}); ok { + return v + } + // Если это []string, преобразуем каждый элемент к interface{} + if v, ok := val.([]string); ok { + // Создаем новый срез с типом []interface{} + result := make([]interface{}, len(v)) + // Преобразуем каждый элемент []string в interface{} + for i, s := range v { + result[i] = s + } + return result + } + // Если это []int, преобразуем каждый элемент к interface{} + if v, ok := val.([]int); ok { + // Создаем новый срез с типом []interface{} + result := make([]interface{}, len(v)) + // Преобразуем каждый элемент []int в interface{} + for i, n := range v { + result[i] = n + } + return result + } + // Если это []float64 (тип, который возвращает json.Unmarshal), + // преобразуем каждый элемент к interface{} + if v, ok := val.([]float64); ok { + // Создаем новый срез с типом []interface{} + result := make([]interface{}, len(v)) + // Преобразуем каждый элемент []float64 в interface{} + for i, n := range v { + result[i] = n + } + return result + } + // Если тип не поддерживается, возвращаем nil + return nil +} + +func (js *JSONStore) GetMap(key string) map[string]interface{} { + val := js.GetValue(key) + if val == nil { + return nil + } + if m, ok := val.(map[string]interface{}); ok { + return m + } + // Попробуем привести через json, если тип не совпал + if b, err := json.Marshal(val); err == nil { + var m map[string]interface{} + if err := json.Unmarshal(b, &m); err == nil { + return m + } + } + return nil +} + +func (js *JSONStore) ValueEquals(a, b interface{}) bool { + return reflect.DeepEqual(a, b) +} + +func (js *JSONStore) SetValue(key string, value interface{}) { + + values := js.load() + oldVal := values[key] + if value == nil { + delete(values, key) + js.save(values) + } else if oldVal == nil || !js.ValueEquals(oldVal, value) { + values[key] = value + js.save(values) + } +} + +func (js *JSONStore) ResetValue(key string) { + + values := js.load() + if _, exists := values[key]; exists { + delete(values, key) + js.save(values) + } +} + +func (js *JSONStore) save(data map[string]interface{}) { + bytes, err := json.MarshalIndent(data, "", " ") + if err != nil { + return + } + _ = os.WriteFile(js.filePath, bytes, 0644) + js.values = data +} diff --git a/part_5/go_json_store/main.go b/part_5/go_json_store/main.go new file mode 100644 index 0000000..e0b6c32 --- /dev/null +++ b/part_5/go_json_store/main.go @@ -0,0 +1,167 @@ +package main + +import ( + "fmt" + + "go_json_store/jsonstore" +) + +func main() { + store := jsonstore.NewJSONStore("store.json") + store.SetValue("map", map[string]int{"a": 1, "b": 2}) + store.SetValue("strList", []string{"a", "b", "c"}) + store.SetValue("intList", []int{1, 2, 3}) + + // Получение и приведение типов + rawStrList := store.GetList("strList") + var strList []string + for _, v := range rawStrList { + if s, ok := v.(string); ok { + strList = append(strList, s) + } + } + + rawIntList := store.GetList("intList") + var intList []int + for _, v := range rawIntList { + if n, ok := v.(int); ok { + intList = append(intList, n) + } + } + + rawMap := store.GetMap("map") + myMap := make(map[string]int) + for k, v := range rawMap { + switch n := v.(type) { + case int: + myMap[k] = n + case float64: + myMap[k] = int(n) + } + } + + fmt.Printf("%T\n", strList) // []string + fmt.Printf("%T\n", intList) // []int + fmt.Printf("%T\n", myMap) // map[string]int + + fmt.Println(strList) // [a b c] + fmt.Println(intList) // [1 2 3] + fmt.Println(myMap) // map[a:1 b:2] + + boolVal := store.GetBool("bool") + fmt.Println(*boolVal) // true + intVal := store.GetInt("int") + fmt.Println(*intVal) // 55 + floatVal := store.GetFloat("double") + fmt.Println(*floatVal) // 99.4 +} + +//*************** 3 *************************** +/* +func main() { + store := jsonstore.NewJSONStore("store.json") + + store.ResetValue("map") + store.ResetValue("str") + + fmt.Println(store.GetValue("map")) // + fmt.Println(store.GetValue("str")) // +} +*/ + +//*************** 2 *************************** +/* +func main() { + store := jsonstore.NewJSONStore("store.json") + + store.SetValue("strList", "-_-") + store.SetValue("double", 99) + + fmt.Println(store.GetValue("strList")) // -_- + fmt.Println(store.GetValue("double")) // 99 +} +*/ + +//*************** 1 *************************** +/* +func main() { + store := jsonstore.NewJSONStore("store.json") + store.SetValue("strList", []string{"a", "b", "c"}) + store.SetValue("int", 55) + store.SetValue("bool", true) + store.SetValue("double", 3.14) + store.SetValue("map", map[string]int{"a": 1, "b": 2}) + store.SetValue("str", "(づ˶•༝•˶)づ♡") + + fmt.Println(store.Values()) + // [[a b c] 55 true 3.14 map[a:1 b:2] (づ˶•༝•˶)づ♡] + + fmt.Println(store.Keys()) + // [bool double map str strList int] + + fmt.Println(store.Contains("strList")) // true + fmt.Println(store.GetValue("strList")) // [a b c] + fmt.Println(store.GetValue("int")) // 55 + fmt.Println(store.GetValue("bool")) // true + fmt.Println(store.GetValue("double")) // 3.14 + fmt.Println(store.GetValue("map")) // map[a:1 b:2] + fmt.Println(store.GetValue("str")) // (づ˶•༝•˶)づ♡ +} */ + +/*func main() { + fmt.Println("Starting JSON Store Demo") + store := jsonstore.NewJSONStore("store.json") + + // Set some values + mapValue := map[string]any{"a": 1, "b": 2} + strList := []any{"a", "b", "c"} + intList := []any{1, 2, 3} + + store.SetValue("map", mapValue) + store.SetValue("strList", strList) + store.SetValue("intList", intList) + + // Get values + retrievedStrList := store.GetList("strList") + retrievedIntList := store.GetList("intList") + retrievedMap := store.GetMap("map") + + // Print results + fmt.Printf("strList type: %T\n", retrievedStrList) + fmt.Printf("intList type: %T\n", retrievedIntList) + fmt.Printf("map type: %T\n", retrievedMap) + + fmt.Printf("strList: %v\n", retrievedStrList) + fmt.Printf("intList: %v\n", retrievedIntList) + fmt.Printf("map: %v\n", retrievedMap) + + // Let's demonstrate more features similar to the commented section in the Dart code + store.SetValue("int", 55) + store.SetValue("bool", true) + store.SetValue("double", 3.14) + store.SetValue("str", "(づ˶•༝•˶)づ♡") + + fmt.Println("\nAll values:", store.Values()) + fmt.Println("All keys:", store.Keys()) + + fmt.Printf("Contains 'strList': %v\n", store.Contains("strList")) + fmt.Printf("Value of 'strList': %v\n", store.GetValue("strList")) + fmt.Printf("Value of 'int': %v\n", store.GetValue("int")) + fmt.Printf("Value of 'bool': %v\n", store.GetValue("bool")) + fmt.Printf("Value of 'double': %v\n", store.GetValue("double")) + fmt.Printf("Value of 'map': %v\n", store.GetValue("map")) + fmt.Printf("Value of 'str': %v\n", store.GetValue("str")) + + // Update values + store.SetValue("strList", "-_-") + store.SetValue("double", 99) + fmt.Printf("\nUpdated 'strList': %v\n", store.GetValue("strList")) + fmt.Printf("Updated 'double': %v\n", store.GetValue("double")) + + // Reset values + store.ResetValue("map") + store.ResetValue("str") + fmt.Printf("After reset, 'map': %v\n", store.GetValue("map")) + fmt.Printf("After reset, 'str': %v\n", store.GetValue("str")) +} +*/ diff --git a/part_5/go_json_store/store.json b/part_5/go_json_store/store.json new file mode 100644 index 0000000..837c154 --- /dev/null +++ b/part_5/go_json_store/store.json @@ -0,0 +1,19 @@ +{ + "bool": true, + "double": 99.4, + "int": 55, + "intList": [ + 1, + 2, + 3 + ], + "map": { + "a": 1, + "b": 2 + }, + "strList": [ + "a", + "b", + "c" + ] +} \ No newline at end of file diff --git a/part_5/tic_tac_toe/.vscode/launch.json b/part_5/tic_tac_toe/.vscode/launch.json new file mode 100644 index 0000000..edd87ae --- /dev/null +++ b/part_5/tic_tac_toe/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + "version": "0.2.0", + "configurations": [ + + { + "name": "Launch Package", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${fileDirname}", // <- ставим запятую + "console": "integratedTerminal" + } + ] +} diff --git a/part_5/tic_tac_toe/game/board.go b/part_5/tic_tac_toe/game/board.go new file mode 100644 index 0000000..ff0290a --- /dev/null +++ b/part_5/tic_tac_toe/game/board.go @@ -0,0 +1,113 @@ +package game + +import ( + "fmt" +) + +const ( + BoardDefaultSize int = 3 + BoardMinSize int = 3 + BoardMaxSize int = 9 +) + +type Board struct { + Board [][]BoardField `json:"board"` + Size int `json:"size"` +} + +func NewBoard(size int) *Board { + board := make([][]BoardField, size) + for i := range board { + board[i] = make([]BoardField, size) + } + return &Board{Board: board, Size: size} +} + +// Отображение игрового поля +func (b *Board) printBoard() { + fmt.Print(" ") + for i := range b.Size { + fmt.Printf("%d ", i+1) + } + fmt.Println() + for i := range b.Size { + fmt.Printf("%d ", i+1) + for j := range b.Size { + switch b.Board[i][j] { + case empty: + fmt.Print(". ") + case cross: + fmt.Print("X ") + case nought: + fmt.Print("O ") + } + } + fmt.Println() + } +} + +// Проверка возможности и выполнения хода +func (b *Board) makeMove(x, y int) bool { + return b.Board[x][y] == empty +} + +func (b *Board) setSymbol(x, y int, player BoardField) bool { + if b.makeMove(x, y) { + b.Board[x][y] = player + return true + } + return false +} + +// Проверка выигрыша +func (b *Board) checkWin(player BoardField) bool { + // Проверка строк и столбцов + for i := range b.Size { + rowWin, colWin := true, true + for j := range b.Size { + if b.Board[i][j] != player { + rowWin = false + } + if b.Board[j][i] != player { + colWin = false + } + } + if rowWin || colWin { + return true + } + } + + // Главная диагональ + mainDiag := true + for i := range b.Size { + if b.Board[i][i] != player { + mainDiag = false + break + } + } + if mainDiag { + return true + } + + // Побочная диагональ + antiDiag := true + for i := range b.Size { + if b.Board[i][b.Size-i-1] != player { + antiDiag = false + break + } + } + return antiDiag +} + +// Проверка на ничью +func (b *Board) checkDraw() bool { + for i := range b.Size { + for j := range b.Size { + if b.Board[i][j] == empty { + return false + } + } + } + return true +} diff --git a/part_5/tic_tac_toe/game/board_cell_type.go b/part_5/tic_tac_toe/game/board_cell_type.go new file mode 100644 index 0000000..6763716 --- /dev/null +++ b/part_5/tic_tac_toe/game/board_cell_type.go @@ -0,0 +1,10 @@ +package game + +type BoardField int + +// фигуры в клетке поля +const ( + empty BoardField = iota + cross + nought +) diff --git a/part_5/tic_tac_toe/game/game.go b/part_5/tic_tac_toe/game/game.go new file mode 100644 index 0000000..4934b45 --- /dev/null +++ b/part_5/tic_tac_toe/game/game.go @@ -0,0 +1,122 @@ +package game + +import ( + "bufio" + "fmt" + "strconv" + "strings" +) + +type Game struct { + Board *Board `json:"board"` + Player *Player `json:"player"` + Reader *bufio.Reader `json:"-"` + State GameState `json:"state"` + Saver IGameSaver `json:"-"` +} + +func NewGame(board Board, player Player, + reader *bufio.Reader, saver IGameSaver) *Game { + return &Game{ + Board: &board, + Player: &player, + Reader: reader, + State: playing, + Saver: saver, + } +} + +func (g *Game) updateState() { + if g.Board.checkWin(g.Player.Figure) { + if g.Player.Figure == cross { + g.State = crossWin + } else { + g.State = noughtWin + } + } else if g.Board.checkDraw() { + g.State = draw + } +} + +func (g *Game) saveCheck(input string) bool { + if input == "save" { + fmt.Println("Enter file name: ") + fileName, err := g.Reader.ReadString('\n') + if err != nil { + fmt.Println("Invalid input. Please try again.") + return false + } + + fileName = strings.TrimSpace(fileName) + err = g.Saver.SaveGame(fileName, g) + if err != nil { + fmt.Println("Error saving game.") + return false + } + fmt.Println("Game saved successfully!!!") + return true + } + + return false +} + +// Игровой цикл +func (g *Game) Play() { + for g.State == playing { + g.Board.printBoard() + fmt.Printf( + "%s's turn. Enter row and column (e.g. 1 2): ", + g.Player.getSymbol()) + + input, err := g.Reader.ReadString('\n') + input = strings.TrimSpace(input) + if err != nil { + fmt.Println("Invalid input. Please try again.") + continue + } + + input = strings.TrimSpace(input) + if input == "q" { + g.State = quit + break + } + + if g.saveCheck(input) { + continue + } + + parts := strings.Fields(input) + if len(parts) != 2 { + fmt.Println("Invalid input. Please try again.") + continue + } + + row, err1 := strconv.Atoi(parts[0]) + col, err2 := strconv.Atoi(parts[1]) + if err1 != nil || err2 != nil || + row < 1 || col < 1 || row > g.Board.Size || + col > g.Board.Size { + fmt.Println("Invalid input. Please try again.") + continue + } + if g.Board.setSymbol(row-1, col-1, g.Player.Figure) { + g.updateState() + g.Player.switchPlayer() + + } else { + fmt.Println("This cell is already occupied!") + } + } + + g.Board.printBoard() + + if g.State == crossWin { + fmt.Println("X wins!") + } else if g.State == noughtWin { + fmt.Println("O wins!") + } else if g.State == draw { + fmt.Println("It's a draw!") + } else { + fmt.Println("Game over!") + } +} diff --git a/part_5/tic_tac_toe/game/game_state.go b/part_5/tic_tac_toe/game/game_state.go new file mode 100644 index 0000000..2539e8f --- /dev/null +++ b/part_5/tic_tac_toe/game/game_state.go @@ -0,0 +1,12 @@ +package game + +type GameState int + +// состояние игрового процесса +const ( + playing GameState = iota + draw + crossWin + noughtWin + quit +) diff --git a/part_5/tic_tac_toe/game/i_game_loader.go b/part_5/tic_tac_toe/game/i_game_loader.go new file mode 100644 index 0000000..c598d13 --- /dev/null +++ b/part_5/tic_tac_toe/game/i_game_loader.go @@ -0,0 +1,9 @@ +package game + +type IGameLoader interface { + LoadGame(path string) (*Game, error) +} + +type IGameSaver interface { + SaveGame(path string, game *Game) error +} diff --git a/part_5/tic_tac_toe/game/json_game_loader.go b/part_5/tic_tac_toe/game/json_game_loader.go new file mode 100644 index 0000000..8778c6a --- /dev/null +++ b/part_5/tic_tac_toe/game/json_game_loader.go @@ -0,0 +1,50 @@ +package game + +import ( + "encoding/json" + "os" + "strings" +) + +func NewJsonGameLoader() IGameLoader { + return &JsonGameLoader{} +} + +type JsonGameLoader struct{} + +func (j *JsonGameLoader) LoadGame(path string) (*Game, error) { + if !strings.HasSuffix(path, ".json") { + path += ".json" + } + file, err := os.Open(path) + if err != nil { + return nil, err + } + defer file.Close() + + decoder := json.NewDecoder(file) + var game Game + err = decoder.Decode(&game) + if err != nil { + return nil, err + } + return &game, nil +} + +func (j *JsonGameLoader) SaveGame(path string, game *Game) error { + if !strings.HasSuffix(path, ".json") { + path += ".json" + } + file, err := os.Create(path) + if err != nil { + return err + } + defer file.Close() + + encoder := json.NewEncoder(file) + err = encoder.Encode(game) + if err != nil { + return err + } + return nil +} diff --git a/part_5/tic_tac_toe/game/player.go b/part_5/tic_tac_toe/game/player.go new file mode 100644 index 0000000..2441444 --- /dev/null +++ b/part_5/tic_tac_toe/game/player.go @@ -0,0 +1,24 @@ +package game + +type Player struct { + Figure BoardField `json:"figure"` +} + +func NewPlayer() *Player { + return &Player{Figure: cross} +} + +func (p *Player) switchPlayer() { + if p.Figure == cross { + p.Figure = nought + } else { + p.Figure = cross + } +} + +func (p *Player) getSymbol() string { + if p.Figure == cross { + return "X" + } + return "O" +} diff --git a/part_5/tic_tac_toe/go.mod b/part_5/tic_tac_toe/go.mod new file mode 100644 index 0000000..ced943a --- /dev/null +++ b/part_5/tic_tac_toe/go.mod @@ -0,0 +1,3 @@ +module tic-tac-toe + +go 1.24.0 diff --git a/part_5/tic_tac_toe/main.go b/part_5/tic_tac_toe/main.go new file mode 100644 index 0000000..5d7de46 --- /dev/null +++ b/part_5/tic_tac_toe/main.go @@ -0,0 +1,76 @@ +package main + +import ( + "bufio" + "fmt" + "os" + "strconv" + "strings" + "tic-tac-toe/game" +) + +func main() { + reader := bufio.NewReader(os.Stdin) + loader := game.NewJsonGameLoader() + boardSize := 0 + + for { + fmt.Println("1 - load game") + fmt.Println("2 - new game") + fmt.Println("q - quit") + + input, _ := reader.ReadString('\n') + input = strings.TrimSpace(input) + switch input { + case "1": + var loadedGame *game.Game + var err error + for { + fmt.Println("Enter file name: ") + fileName, _ := reader.ReadString('\n') + fileName = strings.TrimSpace(fileName) + loadedGame, err = loader.LoadGame(fileName) + if err != nil { + fmt.Println("Error loading game.") + continue + } + break + } + loadedGame.Reader = reader + loadedGame.Saver = loader.(game.IGameSaver) + loadedGame.Play() + case "2": + for { + fmt.Print("Enter the size of the board (3-9): ") + input, err := reader.ReadString('\n') + if err != nil { + fmt.Println("Error reading input.") + continue + } + input = strings.TrimSpace(input) + boardSize, err = strconv.Atoi(input) + if err != nil { + // Использовать предыдущий размер по умолчанию + boardSize = game.BoardDefaultSize + } + if boardSize < game.BoardMinSize || + boardSize > game.BoardMaxSize { + fmt.Println("Invalid board size.") + } else { + break + } + } + + board := game.NewBoard(boardSize) + player := game.NewPlayer() + game := game.NewGame(*board, *player, reader, + loader.(game.IGameSaver)) + game.Play() + case "q": + return + default: + fmt.Println("Invalid input. Please try again.") + return + } + } +} diff --git a/part_5/tic_tac_toe/myGame.json b/part_5/tic_tac_toe/myGame.json new file mode 100644 index 0000000..8b2b247 --- /dev/null +++ b/part_5/tic_tac_toe/myGame.json @@ -0,0 +1 @@ +{"board":{"board":[[0,2,0,0],[0,0,1,0],[0,0,0,0],[0,0,1,0]],"size":4},"player":{"figure":2},"state":0}