golang路上的小学生系列--使用reflect查找package路径
本文同时发布在个人博客chinazt.cc 和 gitbook
今日看到了一个有趣的golang项目--kolpa(https://github.com/malisit/kolpa)。 这个项目可以用来生成伪造的姓名,地址,时间,User-Agent等等信息,在需要大量随机数据的测试环境中非常合适。
点击fork之后,放在本地环境中build,run结果失败。运行项目中提供的demo也失败,按道理来说官方提供的demo应该都会成功,而且自己也没有修改任何一行代码,失败是不科学的。
所以只能剖解代码,查找失败原因。
一查不知道,原来此项目需要依赖各个语言环境下的模板文件,而模板文件都放在项目的data目录中。 在代码中,通过硬编码来确定模板文件位置:
// Reads the file "fName" and returns its content as a slice of strings.
func (g *Generator) fileToSlice(fName string) ([]string, error) {
var res []string
path := os.Getenv("GOPATH") + "/src/github.com/malisit/kolpa/data/" + g.Locale + "/" + fName
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
res = append(res, scanner.Text())
}
if err := scanner.Err(); err != nil {
//log.Println("Inteded generation is not valid for selected language. Switching to en_US.")
return g.fileToSlice(fName)
}
return res, nil
}
因为我是通过fork,然后clone到本地的方式来运行demo,本地此时packge路径已经不是上面的路径了,所以导致运行失败。 很难说这是一个bug,但的确影响到了程序运行。 所以说不良的代码风格更为恰当吧。
既然找到了问题,那下一步就是如何解决问题。 应该如何在Runtime时实时获取package位置呢?
说到Runtime,那么一定就少不了Reflect package。
Package reflect implements run-time reflection, allowing a program to manipulate objects with arbitrary types. The typical use is to take a value with static type interface{} and extract its dynamic type information by calling TypeOf, which returns a Type.
从Reflect的介绍上看,Reflect package推荐的使用方式是通过TypeOf返回一个带有interface{}所有动态类型信息的Type类型,然后通过Type类型来获取各种程序需要的信息。
type Type interface {
// Align returns the alignment in bytes of a value of
// this type when allocated in memory.
Align() int
// FieldAlign returns the alignment in bytes of a value of
// this type when used as a field in a struct.
FieldAlign() int
// Method returns the i'th method in the type's method set.
// It panics if i is not in the range [0, NumMethod()).
//
// For a non-interface type T or *T, the returned Method's Type and Func
// fields describe a function whose first argument is the receiver.
//
// For an interface type, the returned Method's Type field gives the
// method signature, without a receiver, and the Func field is nil.
Method(int) Method
// MethodByName returns the method with that name in the type's
// method set and a boolean indicating if the method was found.
//
// For a non-interface type T or *T, the returned Method's Type and Func
// fields describe a function whose first argument is the receiver.
//
// For an interface type, the returned Method's Type field gives the
// method signature, without a receiver, and the Func field is nil.
MethodByName(string) (Method, bool)
// NumMethod returns the number of exported methods in the type's method set.
NumMethod() int
// Name returns the type's name within its package.
// It returns an empty string for unnamed types.
Name() string
// PkgPath returns a named type's package path, that is, the import path
// that uniquely identifies the package, such as "encoding/base64".
// If the type was predeclared (string, error) or unnamed (*T, struct{}, []int),
// the package path will be the empty string.
PkgPath() string
// Size returns the number of bytes needed to store
// a value of the given type; it is analogous to unsafe.Sizeof.
Size() uintptr
// String returns a string representation of the type.
// The string representation may use shortened package names
// (e.g., base64 instead of "encoding/base64") and is not
// guaranteed to be unique among types. To test for type identity,
// compare the Types directly.
String() string
// Kind returns the specific kind of this type.
Kind() Kind
// Implements reports whether the type implements the interface type u.
Implements(u Type) bool
// AssignableTo reports whether a value of the type is assignable to type u.
AssignableTo(u Type) bool
// ConvertibleTo reports whether a value of the type is convertible to type u.
ConvertibleTo(u Type) bool
// Comparable reports whether values of this type are comparable.
Comparable() bool
// Bits returns the size of the type in bits.
// It panics if the type's Kind is not one of the
// sized or unsized Int, Uint, Float, or Complex kinds.
Bits() int
// ChanDir returns a channel type's direction.
// It panics if the type's Kind is not Chan.
ChanDir() ChanDir
// IsVariadic reports whether a function type's final input parameter
// is a "..." parameter. If so, t.In(t.NumIn() - 1) returns the parameter's
// implicit actual type []T.
//
// For concreteness, if t represents func(x int, y ... float64), then
//
// t.NumIn() == 2
// t.In(0) is the reflect.Type for "int"
// t.In(1) is the reflect.Type for "[]float64"
// t.IsVariadic() == true
//
// IsVariadic panics if the type's Kind is not Func.
IsVariadic() bool
// Elem returns a type's element type.
// It panics if the type's Kind is not Array, Chan, Map, Ptr, or Slice.
Elem() Type
// Field returns a struct type's i'th field.
// It panics if the type's Kind is not Struct.
// It panics if i is not in the range [0, NumField()).
Field(i int) StructField
// FieldByIndex returns the nested field corresponding
// to the index sequence. It is equivalent to calling Field
// successively for each index i.
// It panics if the type's Kind is not Struct.
FieldByIndex(index []int) StructField
// FieldByName returns the struct field with the given name
// and a boolean indicating if the field was found.
FieldByName(name string) (StructField, bool)
// FieldByNameFunc returns the struct field with a name
// that satisfies the match function and a boolean indicating if
// the field was found.
//
// FieldByNameFunc considers the fields in the struct itself
// and then the fields in any anonymous structs, in breadth first order,
// stopping at the shallowest nesting depth containing one or more
// fields satisfying the match function. If multiple fields at that depth
// satisfy the match function, they cancel each other
// and FieldByNameFunc returns no match.
// This behavior mirrors Go's handling of name lookup in
// structs containing anonymous fields.
FieldByNameFunc(match func(string) bool) (StructField, bool)
// In returns the type of a function type's i'th input parameter.
// It panics if the type's Kind is not Func.
// It panics if i is not in the range [0, NumIn()).
In(i int) Type
// Key returns a map type's key type.
// It panics if the type's Kind is not Map.
Key() Type
// Len returns an array type's length.
// It panics if the type's Kind is not Array.
Len() int
// NumField returns a struct type's field count.
// It panics if the type's Kind is not Struct.
NumField() int
// NumIn returns a function type's input parameter count.
// It panics if the type's Kind is not Func.
NumIn() int
// NumOut returns a function type's output parameter count.
// It panics if the type's Kind is not Func.
NumOut() int
// Out returns the type of a function type's i'th output parameter.
// It panics if the type's Kind is not Func.
// It panics if i is not in the range [0, NumOut()).
Out(i int) Type
// contains filtered or unexported methods
}
在Type接口定义中,和Package Path有关的有两个函数:
- PkgPath()
- String()
PkgPath 返回指定类型的import package path,也就是说,如果代码中有import encoding/base64这样的语句, 那么通过PkgPath()就会返回encoding/base64,而不是base64package所在的实际路径。 反言之,PkgPath()返回的是import package path。
而如果继续使用上例中的encoding/base64来说,String()返回的是base64,而不是encoding/base64。 String()返回的是实际使用的package name。
所以总结如下:
- PkgPath() 返回import package path
- String() 返回import package name
显然,PkgPath()更适合我们的需求。 简单改造代码如下:
1.增加 Pkg path
// Generator struct to access various generator functions
type Generator struct {
Locale string
Pkg string
}
2.获取Pkg
// C is the creator function, initiates kolpa with or without locale
// setting. The default locale setting is "en_US".
// Returns a generator type that will be used to call generator methods.
func C(localeVar ...string) Generator {
newGenerator := Generator{}
if len(localeVar) > 0 {
newGenerator.Locale = localeVar[0]
} else {
newGenerator.Locale = "en_US"
}
// newGenerator.populateFunctions()
newGenerator.Pkg = reflect.TypeOf(newGenerator).PkgPath()
return newGenerator
}
3.替换硬编码
// Reads the file "fName" and returns its content as a slice of strings.
func (g *Generator) fileToSlice(fName string) ([]string, error) {
var res []string
path := os.Getenv("GOPATH") + "/src/" + g.Pkg + "/data/" + g.Locale + "/" + fName
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
res = append(res, scanner.Text())
}
if err := scanner.Err(); err != nil {
//log.Println("Inteded generation is not valid for selected language. Switching to en_US.")
return g.fileToSlice(fName)
}
return res, nil
}
4.运行demo
⋊> ~/S/g/g/s/t/kopla ./kopla
石洁玉
幸和平
完美生成两个随机姓名,话说"和平"也是一代人经常起的名字。
最后提一句,有的地方曾经说到尽量少使用Reflect package。 因此反射使用多了,影响效率。 我想这种想法应该是从Java VM那里流传出来的吧。 因为Java VM负责将字节码翻译成机器码,因此频繁调用反射会加重VM切换上下文的代价,也就是把装载期做的事情搬到了运行期,势必降低运行效率。 golang的反射减少了翻译环节,同时借助于编译器进行了代码优化,虽然同样在反射时需要进行额外的安全检查和类型检查,但不会降低太多效率。 而且上面,我们也看到只有在调用时也只发生了一次反射调用,影响几乎可忽略不计。
golang路上的小学生系列--使用reflect查找package路径的更多相关文章
- Java成神路上之设计模式系列教程之一
Java成神路上之设计模式系列教程之一 千锋-Feri 在Java工程师的日常中,是否遇到过如下问题: Java 中什么叫单例设计模式?请用Java 写出线程安全的单例模式? 什么是设计模式?你是否在 ...
- Python引用模块和查找模块路径
模块间相互独立相互引用是任何一种编程语言的基础能力.对于"模块"这个词在各种编程语言中或许是不同的,但我们可以简单认为一个程序文件是一个模块,文件里包含了类或者方法的定义.对于编译 ...
- Python入门之Python引用模块和查找模块路径
#这篇文章主要介绍了Python引用模块和Python查找模块路径的相关资料,需要的朋友可以参考下 模块间相互独立相互引用是任何一种编程语言的基础能力.对于“模块”这个词在各种编程语言中或许是不同的, ...
- springboot查找配置文件路径的过程
spring加载配置文件是通过listener监视器实现的,在springboot启动时: 在容器启动完成后会广播一个SpringApplicationEvent事件,而SpringApplicati ...
- 查找文件路径find
查找文件路径find 1.按照文件名查找 (1)find / -name httpd.conf #在根目录下查找文件httpd.conf,表示在整个硬盘查找 (2)find ...
- 在Python中使用glob模块查找文件路径的方法
在Python中使用glob模块查找文件路径的方法 glob模块是最简单的模块之一,内容非常少.用它可以查找符合特定规则的文件路径名.跟使用windows下的文件搜索差不多.查找文件只用到三个匹配符: ...
- 初识TypeScript:查找指定路径下的文件按类型生成json
如果开发过node.js的话应该对js(javascript)非常熟悉,TypeScript(以下简称ts)是js的超集. 下面是ts的官网: https://www.tslang.cn/ 1.环境配 ...
- Golang爬虫示例包系列教程(一):pedaily.com投资界爬虫
Golang爬虫示例包 文件结构 自己用Golang原生包封装了一个爬虫库,源码见go get -u -v github.com/hunterhug/go_tool/spider ---- data ...
- golang数据结构和算法之BinarySearch二分查找法
基础语法差不多了, 就需要系统的撸一下数据结构和算法了. 没找到合适的书, 就参考github项目: https://github.com/floyernick/Data-Structures-and ...
随机推荐
- 关于ubuntu的图标创建以及快捷方式打开
//这里个人要添加的的为微信小程序开发工具 1:终端命令: sudo gedit /usr/share/applications/MyChat.desktop 2:修改启动器配置如下: [Deskto ...
- Nginx教程(四) Location配置与ReWrite语法
Nginx教程(四) Location配置与ReWrite语法 1 Location语法规则 1.1 Location规则 语法规则: location [=|~|~*|^~] /uri/ {- } ...
- 使用PostProcessBuild设定Unity产生的Xcode Project
简单来说就是unity提供一套api去修改xcode项目工程配置以及修改plist文件内容(当unity build结束后, 会自动回调OnPostProcessBuild). 以下是一些用到的配置处 ...
- 模仿ICE的structured panorama小按钮
这个按钮的目的是用于手动排列图片序列,应该说写得比较精巧,我使用csharp进行模仿,主要采用的是自动控件创建技术.结果比较简陋,实现功能而已,放出来大家一起学习. ; ; ...
- How and when: ridge regression with glmnet
@drsimonj here to show you how to conduct ridge regression (linear regression with L2 regularization ...
- React,关于redux的一点小见解
最近项目做多页面应用使用到了,react + webpack + redux + antd去构建多页面的应用,本地开发用express去模拟服务端程序(个人觉得可以换成dva).所以在这里吐槽一下我自 ...
- ECMAScript 6.0 简介
ECMAScript 6.0 在es6中有 许多语法.还有lambda的使用.以及 class 的使用 还有一些新的对象来解决一些事情 可以提高开发效率 但更重要的是 颠覆 javascript 在你 ...
- Win95+IE3 – Win10+IE11全版本执行漏洞(含POC)
微软本月安全更新修复了一个潜藏了18年的IE远程代码执行漏洞(CVE-2014-6332),可以说是给windows吃了一颗大补丸.缺陷出现在VBScript的代码中,自Windows 95首次发布( ...
- 初学vitmio,vitmio的环境配置及遇到的坑
1 到官网或者github下载vitamio 官网地址:https://www.vitamio.org/ github地址:https://github.com/yixia/VitamioBundle ...
- iptables 基本用法
iptables 1.iptables 表 (1)介绍常用表 filtert(过滤器) 链 ↓ INPUT chain:控制进入主机的数据包 OUTPUT chain:控制向外发出的数据包 FORWA ...