• Почему файлы открываются нестабильно?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Подозреваю, что не работает os.OpenFile, а ioutil.ReadFile работает.
    И скорее всего дело в правах os.OpenFile("file.txt", os.O_RDWR, 0666).

    Начните с того, что обработайте ошибку, чтобы можно было понять что именно не так.
    f, err := os.OpenFile("file.txt", os.O_RDWR, 0666)
    if err != nil {
        log.Fatalln(err)
    }


    PS: ioutil.ReadFile("file.txt") для открытия файла вызывает функцию OpenFile(name, O_RDONLY, 0)
    Ответ написан
  • Как выполняется валидация покупок?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Судя по методам интерфейса Storage нужен для того, чтобы эта библиотека имела возможность сохранять в вашу базу данные подписки/оплаты.

    Можете начать с того, что сделаете пустую реализацию, посмотрите какие параметры вам будут приходить/какие будут нужны и там уже разберётесь как вам удобнее сохранить данные.
    Допустим вы будете хранить данные в MySQL... тогда реализация может выглядеть вот так
    type MySQLStorage struct {
        db *sql.DB
    }
    
    func NewMySQLStorage(db *sql.DB) *MySQLStorage {
        return &MySQLStorage{
            db: db,
        }
    }
    
    func (sg *MySQLStorage) StorePurchases(ctx context.Context, sp []*Purchase) ([]*Purchase, error) {
        for _, p := range sp {
            fmt.Printf("%+v\n", p)
        }
    
        return sp, nil
    }
    
    func (sg *MySQLStorage) StoreSubscriptionPurchases(ctx context.Context, sp []*SubscriptionPurchase) ([]*SubscriptionPurchase, error) {
        for _, p := range sp {
            fmt.Printf("%+v\n", p)
        }
    
        return sp, nil
    }

    т.е. вызов может быть таким
    db := connectToDB...
    sg := NewMySQLStorage(db)
    NewValidate(sg  "",  googleConfig)
    Ответ написан
    Комментировать
  • Как правильно повторно измерить время обхода директори?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Скорее все каждый раз измерение показывает верные результаты несмотря на такую разницу.
    Думаю дело в кеше данных с диска, за счёт кеширования повторные обходы работают быстрее.
    Чтобы можно было точнее сказать - нужно видеть ваш код.
    Ответ написан
  • Test in golang: simple function?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Как вариант можно что-то типа такого
    package main
    
    import "testing"
    
    func Test_sum(t *testing.T) {
    
        tt := []struct {
            args GetArgs
            sum int
        }{
            {Args {2, 4}, 6},
            {Args {2, -2}, 0},
        }
    
        for _, tc := range tt {
            tci := tc.args.(GetArgs)
            s := Sum(tci)
            if s != tc.sum {
                t.Errorf("sum of %v and %v should be %v, received- %v", tc.args.GetA(), tc.args.GetB(), tc.sum, s)
            }
        }
    }
    Ответ написан
    Комментировать
  • Почему при открытии файла срабатывает ошибка "The system cannot find the file specified"?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Предполагаю, что после замены os.Open(fileDir.Name()) на os.Open(path.Join(DirFiles, fileDir.Name())) всё заработает.

    Думаю, что проблема вот в чём.
    Скорее всего у вас структура файлов близка к этой
    ./files/file1
    main.go
    Вы получаете список файлов из папки ./files/, и в os.Open передаётся только название файла (file1.go), т.е. os.Open пытается открыть его в текущей папке, а не в папке ./files/, по этому и не может его открыть.
    А если передать в os.Open("./files/file1") - тогда всё заработает (при условии, что есть права на это)
    Ответ написан
  • Сколько занимает в памяти Struct{}{}?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Всё верно, она занимает 0 байт.
    Можно проверить:
    package main
    
    import (
        "fmt"
        "unsafe"
    )
    
    type S1 struct {
        f1 int
    }
    
    func main() {
        s1 := S1{}
        s2 := struct{}{}
    
        fmt.Printf("s1 size: %v\n", unsafe.Sizeof(s1))
        fmt.Printf("s2 size: %v\n", unsafe.Sizeof(s2))
    }

    Вывод
    s1 size: 8
    s2 size: 0


    Там, где в мапе нужны только ключи - я всегда делаю значение struct{}{}
    Ответ написан
    4 комментария
  • Как привести тип os.FileInfo к типу string?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    ioutil.ReadDir возвращает []fs.FileInfo
    Это интерфейс, выглядит вот так
    type FileInfo interface {
    	Name() string       // base name of the file
    	Size() int64        // length in bytes for regular files; system-dependent for others
    	Mode() FileMode     // file mode bits
    	ModTime() time.Time // modification time
    	IsDir() bool        // abbreviation for Mode().IsDir()
    	Sys() interface{}   // underlying data source (can return nil)
    }


    Т.е. в вашем случае filepath.Ext(string(fileDir)) можно заменить на filepath.Ext(fileDir.Name())
    Это то, что нужно или нужно что-то другое?
    По примеру кода не совсем понятно для чего вам нужно приведение к os.FileInfo/string.

    Посмотрел os.FileInfo
    Он определён как type FileInfo = fs.FileInfo
    Т.е. грубо это тот же интерфейс, методы которого можно вызывать точно также.
    Ответ написан
  • Как сделать поиск файлов только в одной папке; указать текущую директорию?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Чтобы указать текущую папку - можно указать "точку" в качестве имени папки, чтобы не было рекурсии - воспользуйтесь функцией ioutil.ReadDir.
    package main
    
    import (
        "fmt"
        "io/ioutil"
        "log"
    )
    
    func main() {
        // текущая папка
        files, err := ioutil.ReadDir(".")
        if err != nil {
            log.Fatal(err)
        }
    
        for _, file := range files {
            fmt.Println(file.Name(), file.IsDir())
        }
    }
    Ответ написан
    5 комментариев
  • Поиск по байтовому массиву?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Думаю это сильно зависит от того, как часто вам нужно делать поиск в этом файле.
    Если не часто - тогда можно просто вычитывать данные порциями, и искать последовательность в них.
    Если же очень часто - нужно понять что там за данные и думать как сделать индекс для поиска в этом файле.

    Как вариант, можно запустить несколько горутин.
    Т.е. пока одна горутина читает одну порцию данных из файла в память - другая ищет в предыдущей порции нужную вам последовательность.

    Еще, как вариант, можно сделать простенький индекс (упрощаю, чтобы было легче писать понятный пример)

    Допустим у нас есть файл с таким содержимым
    0x02 0x09 0x02 0x05 0x09 0x09, 0xf1 0xfa 0x02

    Найти нужно последовательность 0x02 0x05

    Строим индекс [массив байт от 0x00 до 0xff][слайс адресов этого байта в файле]
    т.е.
    [значение байта][адрес этого байта в файле1, адрес этого байта в файле2, адрес этого байта в файле3...]
    Для указанного файла индекс получится вот такой
    [0x00][]
    [0x01][]
    [0x02][0x00, 0x02, 0x08]
    ...
    [0x05][0x03]
    ...
    [0x09][0x01, 0x04, 0x05]
    ...
    [0xf1][0x06]
    ...
    [0xfa][0x07]
    ...

    Первый байт в искомой последовательности 0x02 0x05 - будет 0x02.
    Мы берём слайс в массиве по индексу 0x02 и получаем список адресов где в файле есть 0x02 - [0x00, 0x02, 0x08]
    А дальше вы проверяете вашу последовательность только начиная с полученных смещений
    Т.е. проверяем следующий байт по адресу 0x00 + 1 == 0x05 (второму символу последовательности) или нет.
    Если нет - значит берём след. смещение 0х02 и опять проверяем равен ли след. символ 0x05.
    Равен, значит нашли последовательность.
    Ответ написан
  • Ошибка ERROR: column "order" is of type json[] but expression is of type record go gorm что делать?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Скорее всего должно быть как то так
    type Order struct {
      ID uint `gorm:"primaryKey;autoIncrement;unique"json:"id"`
      Owner string `json:"owner"`
      Status string `gorm:"default:ordered"json:"status"`
      Phone int `json:"phone"`
      Order []OrderProduct `gorm:"foreignKey:OrderId"json:"products"`
    }
    
    type OrderProduct struct {
      ID uint `gorm:"primaryKey;autoIncrement;unique"json:"id"`
       // добавляем это поле, чтобы была связь с таблицей Orders
      OrderID uint `json:"-"`
      Brand string `json:"brand"`
      ItemId int `json:"item_id"`
      Img string `json:"img"`
      ItemTotal int `json:"item_total"`
      Price string `json:"price"`
      Quantity int `json:"quantity"`
      Title string `json:"title"`
      Type string `json:"type"`
    }
    Ответ написан
  • Как синхронизировать горутины?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Горутины синхронизировать/обмениваться данными можно при помощи каналов.
    У вас не очень удачный пример для обучения.
    Обычно есть горутина или несколько, которые получают данные и пишут их в канал и есть горутина или несколько, которые будут читать из канала.
    Читающие горутины будут получать данные из канала в той последовательности, в которой они туда попали.
    Ближе к реальности пример будет выглядеть вот так.
    Одна горутина пишет данные в канал, а две горутины по очереди извлекают данные.
    package main
    
    import (
        "fmt"
        "sync"
        "time"
    )
    
    func main() {
        var wg sync.WaitGroup
    
        num := make(chan int, 1000) // 1000 - размер буффера канала
    
        wg.Add(1)
        go func() {
            for i := 0; i < 1000; i++ {
                num <- i
                fmt.Printf("write to channel: %d\n", i)
                // задержка нужна только на время теста
                time.Sleep(100 * time.Microsecond)
            }
            close(num)
            wg.Done()
        }()
    
        wg.Add(1)
        go func() {
            for {
                val, ok := <-num
                if !ok {
                    break
                }
                fmt.Printf("goroutine 1 got: %d\n", val)
            }
            wg.Done()
        }()
    
        wg.Add(1)
        go func() {
            for {
                val, ok := <-num
                if !ok {
                    break
                }
                fmt.Printf("goroutine 2 got: %d\n", val)
            }
            wg.Done()
        }()
    
        wg.Wait()
    }
    Ответ написан
    7 комментариев
  • Как обновлять поля в бд которые не являются пустой строкой go gorm?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Мне такого не встречалось, но, в принципе, при желании, можно сделать при помощи рефлексии.
    Но за рефлексию придётся заплатить производительностью :)
    Если нужен пример реализации - дайте знать, сделаю.
    Ответ написан
    4 комментария
  • Почему получаю только первый продукт из таблицы gorm go?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Попробуйте вот так
    func GetProducts(c *gin.Context) {
      var products []models.Product
      database.DB.Find(&products)
      c.JSON(http.StatusOK, products)
    }
    Ответ написан
    3 комментария
  • Нормально ли я отрефакторил if-else?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Я бы посоветовал оставить начальный вариант, т.к. если сделаете banchmark'и - вы увидите, что он будет быстрее вашей реализации.
    Т.к. в начальной реализации нет доп. вызова функции, а в вашей есть getRulesResult.
    Чтобы вызвать функцию - нужно положить значения в стек, а по завершению извлечь.
    За счёт этого ваша реализация будет работать медленнее.
    Но это замечание справедливо только для функций, скорость выполнения которых критична, как сортировка например.

    Если бы это была бизнес логика - то тут приоритет был бы уже на понятность кода в первую очередь, чтобы потом не запутаться, и чтобы кто-то потом мог разобраться с кодом.

    Но и визуально первый вариант понятнее.
    Оцениваю просто по времени, которое нужно затратить на то, чтобы понять как работает начальная реализация и ваша, на начальную ушло меньше времени.

    При рефакторинге обычно стараются получить или более понятный/читабельный/расширяемый код, или более производительный.

    Но вы однозначно на верном пути, принятые решения и подход мне нравятся, просто у вас не совсем удачная задача для рефакторинга выбрана, решение изначально хорошее )

    Обычно, код, который нужно рефакторить выглядит во много раз непонятнее, это может быть простыня на несколько экранов монитора :)), а когда в такой простыне еще и цикломатическая сложность огромная (вложенность if'ов и for'ов большая) - такой код вообще очень трудно понять :)
    Вот тут самое то для рефакторинга.
    Ответ написан
    3 комментария
  • Как мне получить body который был отправлен gin gonic golang?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Вот так попробуйте
    jsonDataBytes, err := ioutil.ReadAll(c.Request.Body)
    Ответ написан
    6 комментариев
  • В чем может быть проблема с правами при попытке выполнения компиляции golang?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Вам нужно разобраться правами.

    Если хотите чтобы собиралось от вашего пользователя, не под рутом - тогда
    нужно сделать чтобы вы были владельцем папки с проектом, это можно сделать вот так sudo chown -R ваш_логин /home/work/src и установить правильные права на папку /tmp chmod 777 /tmp

    Под рутом не находит go потому, что его бинарники не в PATH, нужно выполнить что-то типа такого export PATH=$PATH:/usr/lib/golang/bin
    Но нужно сначала найти где у вас лежит Go.
    Можно под обычным пользователем, где Go находит выполнить which go, ответ может быть таким /usr/lib/golang/bin/go - тогда нужно прописать как я написал.
    Если не получится - пишите, постараюсь помочь.
    Ответ написан
  • Как правильно конвертировать тип данных?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Когда вы читаете данные из консоли - они приходят вам с типом string.
    Т.е. сначала нужно считать в строку, а потом уже строку преобразовать во float.

    Вот работающий пример
    package main
    
    import (
        "fmt"
        "log"
        "os"
        "strconv"
    )
    
    func main() {
    
        var what string
        var input string
    
        fmt.Print("Выберите действие (+, -)")
        fmt.Fscan(os.Stdin, &what)
        if what != `+` && what != `-` {
            log.Fatalf("действие указанно не корректно\n")
        }
    
        fmt.Print("Введите первое значение: ")
        fmt.Fscan(os.Stdin, &input)
        a, err := strconv.ParseFloat(input, 64)
        if err != nil {
            log.Fatalf("число указано не корректно: %v\n", err)
        }
    
        fmt.Print("Введите второе значение: ")
        fmt.Fscan(os.Stdin, &input)
        b, err := strconv.ParseFloat(input, 64)
        if err != nil {
            log.Fatalf("число указано не корректно: %v\n", err)
        }
    
        var c float64
        if what == "+" {
            c = a + b
        } else if what == "-" {
            c = a - b
        }
    
        fmt.Printf("Результат: %v\n", c)
    }

    Если это ваши первые шаги в программировании - лучше начать с Python, это ощутимо упростит старт.
    Ответ написан
    2 комментария
  • Gorilla Mux: не берет значение в vars := mux.Vars(r) Golang?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Через mux.Vars вы можете извлечь только то, что в URL, т.е. части URL, а параметры нужно извлекать через r.URL.Query().Get("param_name")

    Давайте разберём пример из реальной задачи.

    В URL содержится английское название категории (chairs), а также параметры фильтров width, height.
    https://domain.com/products/chairs?height=200&width=100


    Т.е. нужно отобразить все товары из категории "стулья" (chairs), но не выше 200 и не шире 100, соответственно нам нужно получить и chairs из URL и параметры.

    Роут будет таким в данном случае
    r.HandleFunc("/products/{category_handle}", h.ShowProducts).Methods("GET")


    А в обработчике будет уже вот так
    // из извлекаем из URL категорию
    vars := mux.Vars(r)
    categoryHandle := vars["categoryHandle"]
    
    // получаем параметры
    height, _ := strconv.Atoi(r.URL.Query().Get("height"))
    width, _ := strconv.Atoi(r.URL.Query().Get("width"))
    Ответ написан
    Комментировать
  • Как на 1 хостинге запустить несколько веб скриптов, на разных субдоменах?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Пишите Go lang сервисы, которые вам нужны.
    Каждый сервис слушает разный порт.
    Ставите веб сервер, например nginx и настраиваете виртуальные хосты, которые прокируют запросы к Go lang сервисам.
    Конфиг nginx'a будет выглядеть примерно так
    server {
        listen *:80;
        server_name yourdomain1.com; # домен, который вам нужно
        ...
        location / {
            proxy_pass http://127.0.0.1:9000; # IP адрес и порт, на котором слушает сервис Go lang.
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
    
    server {
        listen *:80;
        server_name yourdomain2.com; # домен, который вам нужно
        ...
        location / {
            proxy_pass http://127.0.0.1:9001; # IP адрес и порт, на котором слушает сервис Go lang.
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
    Ответ написан
    1 комментарий
  • Как сортировать в Go?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Если нужна именно функция - можно вот так
    func SortBytes(arr []byte) {
        sort.Slice(arr, func(i, j int) bool {
            return arr[i] < arr[j]
        })
    }
    // потом использовать вот так
    arr := []byte{4, 3, 2, 1, 6, 3, 77, 8,3}
    sortBytes(arr)
    fmt.Println(arr)

    Но я бы использовал тот вариант, который у вас )
    sort.Slice(arr, func(i, j int) bool {
      return arr[i] < arr[j]
    })
    fmt.Println(arr)
    Ответ написан
    5 комментариев