Javascript对象与JSON的互转

var obj = JSON.parse(json); //由JSON字符串转换为JSON对象

var json=JSON.stringify(obj); //将JSON对象转化为JSON字符

//此种需下载插件
var json=obj.toJSONString(); //将JSON对象转化为JSON字符

2019/5/6 文章分类: GO语言技巧

golang 1.12 版本的自动补全问题

问题

golang 1.12 开始, 默认的 go install 不再生成 pkg 文件.
所以对第三方库的引用, 无法进行代码的自动补全.

解决方法

go install -i 会生成 pkg 文件夹和编译文件

2019/5/6 文章分类: GO语言技巧

Golang结构体遍历

package main

import (
        "fmt"
        "reflect"
)

type User struct  {
        Id int
        Name string
        //addr string
}

func main(){
        u := User{Id:1001, Name:"xxx"/*, addr:"xxx"*/}
        t := reflect.TypeOf(u)
        v := reflect.ValueOf(u)
        for k := 0; k < t.NumField(); k++ {
                fmt.Printf("%s -- %v \n", t.Field(k).Name, v.Field(k).Interface())   
        }
}

2019/5/3 文章分类: GO语言技巧

golang通过反射获取和设置结构体字段的值

参考一

 type MyStruct struct {
        N int
    }
    n := MyStruct{ 1 }

    // get 
    immutable := reflect.ValueOf(n)
    val := immutable.FieldByName("N").Int()
    fmt.Printf("N=%d\n", val) // prints 1

    // set
    mutable := reflect.ValueOf(&n).Elem()
    mutable.FieldByName("N").SetInt(7)
    fmt.Printf("N=%d\n", n.N) // prints 7
    

参考二

type Person struct {
    Name   string  `json:"name"`
    Age       int   `json:"age"`
}

func SetValueToStruct(name string,age int) *Person {
        p := &Person{}
        v := reflect.ValueOf℗.Elem()
        v.FieldByName("Name").Set(reflect.ValueOf(name))
        v.FieldByName("Age").Set(reflect.ValueOf(age))
        return p
}


func main()  {
    p := SetValueToStruct("张三",18)
    fmt.Println(*p)
}

注:可能出现的错误

1.结构体首字母必须大写,否则会出现:panic: reflect: reflect.Value.Set using value obtained using unexported field
在Golang中首字母的大小写代表着访问权限,首字母小写则包外无法访问
2. 反射需要使用地址 否则会出现:panic: reflect: reflect.Value.Set using unaddressable value
如下代码就会出现panic

p := Person{}
v := reflect.ValueOf℗
v.FieldByName("Name").Set(reflect.ValueOf(name))
v.FieldByName("Age").Set(reflect.ValueOf(age))

2019/5/3 文章分类: GO语言技巧

有点不安全却又一亮的 Go unsafe.Pointer

在上一篇文章 《深入理解 Go Slice》 中,大家会发现其底层数据结构使用了 unsafe.Pointer。因此想着再介绍一下其关联知识。

前言

在大家学习 Go 的时候,肯定都学过 “Go 的指针是不支持指针运算和转换” 这个知识点。为什么呢?

首先,Go 是一门静态语言,所有的变量都必须为标量类型。不同的类型不能够进行赋值、计算等跨类型的操作。那么指针也对应着相对的类型,也在 Compile 的静态类型检查的范围内。同时静态语言,也称为强类型。也就是一旦定义了,就不能再改变它。

错误示例

func main(){
    num := 5
    numPointer := &num

    flnum := (*float32)(numPointer)
    fmt.Println(flnum)
}

输出结果:

# command-line-arguments
...: cannot convert numPointer (type *int) to type *float32

在示例中,我们创建了一个 num 变量,值为 5,类型为 int。取了其对于的指针地址后,试图强制转换为 *float32,结果失败...

unsafe

针对刚刚的 “错误示例”,我们可以采用今天的男主角 unsafe 标准库来解决。它是一个神奇的包,在官方的诠释中,有如下概述:

  • 围绕 Go 程序内存安全及类型的操作

  • 很可能会是不可移植的

  • 不受 Go 1 兼容性指南的保护

简单来讲就是,不怎么推荐你使用。因为它是 unsafe(不安全的),但是在特殊的场景下,使用了它。可以打破 Go 的类型和内存安全机制,让你获得眼前一亮的惊喜效果。

Pointer

为了解决这个问题,需要用到 unsafe.Pointer。它表示任意类型且可寻址的指针值,可以在不同的指针类型之间进行转换(类似 C 语言的 void * 的用途)

其包含四种核心操作:

  • 任何类型的指针值都可以转换为 Pointer

  • Pointer 可以转换为任何类型的指针值

  • uintptr 可以转换为 Pointer

  • Pointer 可以转换为 uintptr

在这一部分,重点看第一点、第二点。你再想想怎么修改 “错误示例” 让它运行起来?

func main(){
    num := 5
    numPointer := &num

    flnum := (*float32)(unsafe.Pointer(numPointer))
    fmt.Println(flnum)
}

输出结果:

0xc4200140b0

在上述代码中,我们小加改动。通过 unsafe.Pointer 的特性对该指针变量进行了修改,就可以完成任意类型(*T)的指针转换。

需要注意的是,这时还无法对变量进行操作或访问。因为不知道该指针地址指向的东西具体是什么类型。不知道是什么类型,又如何进行解析呢。无法解析也就自然无法对其变更了

Offsetof

在上小节中,我们对普通的指针变量进行了修改。那么它是否能做更复杂一点的事呢?

type Num struct{
    i string
    j int64
}

func main(){
    n := Num{i: "EDDYCJY", j: 1}
    nPointer := unsafe.Pointer(&n)

    niPointer := (*string)(unsafe.Pointer(nPointer))
    *niPointer = "煎鱼"

    njPointer := (*int64)(unsafe.Pointer(uintptr(nPointer) + unsafe.Offsetof(n.j)))
    *njPointer = 2

    fmt.Printf("n.i: %s, n.j: %d", n.i, n.j)
}

输出结果:

n.i: 煎鱼, n.j: 2

在剖析这段代码做了什么事之前,我们需要了解结构体的一些基本概念:

  • 结构体的成员变量在内存存储上是一段连续的内存

  • 结构体的初始地址就是第一个成员变量的内存地址

  • 基于结构体的成员地址去计算偏移量。就能够得出其他成员变量的内存地址

再回来看看上述代码,得出执行流程:

  • 修改 n.i 值:i 为第一个成员变量。因此不需要进行偏移量计算,直接取出指针后转换为 Pointer,再强制转换为字符串类型的指针值即可

  • 修改 n.j 值:j 为第二个成员变量。需要进行偏移量计算,才可以对其内存地址进行修改。在进行了偏移运算后,当前地址已经指向第二个成员变量。接着重复转换赋值即可

需要注意的是,这里使用了如下方法(来完成偏移计算的目标):

1、uintptr:uintptr 是 Go 的内置类型。返回无符号整数,可存储一个完整的地址。后续常用于指针运算

type uintptr uintptr

2、unsafe.Offsetof:返回变量的字节大小,也就是本文用到的偏移量大小。需要注意的是入参 ArbitraryType 表示任意类型,并非定义的 int。它实际作用是一个占位符

func Offsetof(x ArbitraryType) uintptr

在这一部分,其实就是巧用了 Pointer 的第三、第四点特性。这时候就已经可以对变量进行操作了

错误示例

func main(){
    n := Num{i: "EDDYCJY", j: 1}
    nPointer := unsafe.Pointer(&n)
    ...

    ptr := uintptr(nPointer)
    njPointer := (*int64)(unsafe.Pointer(ptr + unsafe.Offsetof(n.j)))
    ...
}

这里存在一个问题,uintptr 类型是不能存储在临时变量中的。因为从 GC 的角度来看,uintptr 类型的临时变量只是一个无符号整数,并不知道它是一个指针地址

因此当满足一定条件后,ptr 这个临时变量是可能被垃圾回收掉的,那么接下来的内存操作,岂不成迷?

总结

简洁回顾两个知识点。第一是 unsafe.Pointer 可以让你的变量在不同的指针类型转来转去,也就是表示为任意可寻址的指针类型。第二是 uintptr 常用于与 unsafe.Pointer 打配合,用于做指针运算,巧妙地很。

最后还是那句话,没有特殊必要的话。是不建议使用 unsafe 标准库,它并不安全,虽然它常常能让你眼前一亮。

2019/5/3 文章分类: GO语言技巧

跨平台交叉编译Go程序

作用:比如你手头只有Mac系统,而你的用户有Linux和Windows的,他们也想用,你可以通过交叉编译出Linux和Windows上的可执行文件给他们用

阅读全文   2018/8/14 文章分类: GO语言技巧

go run main.go杀掉进程的方法

可以通过下面的命令来处理:


lsof -i tcp:端口号

kill PID 

也可以通过下面的命令来处理:


killall -9 main

其中的main,就是main.go运行之后的进程名称。

2018/8/12 文章分类: GO语言技巧

Go语言的字符串拼装方式性能对比

Go语言中字符串的拼装方法很多,那么问题来了,到底哪家性能好?

下面代码,分别比较了 fmt.Sprintf,string +,strings.Join,bytes.Buffer,方法是循环若干次比较总时间。

在VMWare下的Ubuntu 14.04下运行的结果表明:

  • fmt.Sprintf 和 strings.Join 速度相当
  • string + 比上述二者快一倍
  • bytes.Buffer又比上者快约400-500倍
阅读全文   2017/6/21 文章分类: GO语言技巧

go中获取各种路径

  1. 执行用户当前所在路径:

    
    os.Getwd()
    
    
  2. 执行程序所在路径:

    执行程序文件相对路径:

    
    file, _ := exec.LookPath(os.Args[0])
    
    
阅读全文   2016/7/22 文章分类: GO语言技巧

操作系统相关操作

  • linux下获取进程信息是使用/proc/pid/

  • 判断当前用户是否是root用户

    
    os.Geteuid() != 0
    
    
2016/7/22 文章分类: GO语言技巧

go 类型转换

  1. 整型到字符串

    s = strconv.Itoa(i) 
    
    s = strconv.FormatInt(int64(i), 10)
    
  2. 字符串到整型

    i, err = strconv.Atoi(s)
    
    i, err = ParseInt(s, 10, 0) 
    
阅读全文   2016/7/22 文章分类: GO语言技巧