造轮子,一个TCP消息构造解析器,欢迎大佬提建议

模块名JStruct

module JStruct
    export B,L,Struct,TypeContainer,Int8b,Int8l,Int16b,Int16l,Int32b,Int32l,Int64b,Int64l,
    Int8ub,Int8ul,Int16ub,Int16ul,Int32ub,Int32ul,Int64ub,Int64ul,CharField,
    Parse,Build
    bigEndian='B'
    littleEndian='L'
    B=BigEndian(T)=(T,bigEndian)
    L=LittleEndian(T) = (T,littleEndian)
    #little endian
    Int8ul=L(UInt8)
    Int16ul=L(UInt16)
    Int32ul=L(UInt32)
    Int64ul=L(UInt64)
    Int8l=L(Int8)
    Int16l=L(Int16)
    Int32l=L(Int32)
    Int64l=L(Int64)
    #big endian
    Int8ub=B(UInt8)
    Int16ub=B(UInt16)
    Int32ub=B(UInt32)
    Int64ub=B(UInt64)
    Int8b=B(Int8)
    Int16b=B(Int16)
    Int32b=B(Int32)
    Int64b=B(Int64)

	
    struct TypeContainer
        keylist::Array
        dic::Dict
    end
	
	struct CharField
		len::Integer
        chars::Array{Char,1}

        function CharField(len::Integer)
			new(len,[])
		end
            
	end
	
	
    struct Struct
        typelist::Array
        keylist::Array

        function Struct(data::TypeContainer)
            typelist=[]
            keylist=[]
            for key in data.keylist
               push!(keylist,key)
               push!(typelist,data.dic[key])
            end
			new(typelist,keylist)
        end
    end
	
	function Build(st::Struct,data::Dict)

	end
	
	function Parse(st::Struct,data::IOBuffer)
	    result=Dict()
		i=1
        while i<=length(st.keylist)
			#处理自定义类型,如字符串
			if !(typeof(st.typelist[i])<:Tuple)
				if typeof(st.typelist[i])==CharField
					j=1
					while j<=st.typelist[i].len
						push!(st.typelist[i].chars,read(data,Char))
						j=j+1
					end
#                     println(st.typelist[i].chars)
					push!(result,st.keylist[i]=>String(st.typelist[i].chars))
				end
                i=i+1
				continue
			end
			tpe=st.typelist[i][1]
			endian=st.typelist[i][2]
			frame=read(data,tpe)
            #如果本机是小端字节序,并且发送的字节是小端序,read时会变成大端字节序,需要再进行一次转换
            #而如果本机是小端字节序,并且发送字节是大端序,则不需要进行任何装换
			if ENDIAN_BOM==0x04030201 && endian==littleEndian
				frame=ntoh(frame)
            #如果本机是大端字节序,并且发送字节是大端序,则不需要进行任何操作
            #如果本机是大端字节序,并且发送字节是小端序,则需要进行一次转换
			elseif ENDIAN_BOM==0x01020304 && endian==bigEndian
				frame=ltoh(frame)
			end
			push!(result,st.keylist[i]=>tpe(frame))
            i=i+1
        end
		result
	end

end


测试:

push!(LOAD_PATH,"D://juliaPros//julia_study//src")

using JStruct

a=Struct(
        TypeContainer(
                      ["b","a","c"],
                      Dict(
                          "b"=>CharField(3),
                          "a"=>Int16ub,
                          "c"=>CharField(6)
                      )
       )
)
println(Parse(a,IOBuffer([0x3b,0x3c,0x3d,0x12,0x13,0x3b,0x3c,0x3d,0x3b,0x3c,0x3d])))


结果:

Dict{Any,Any}("c" => ";<=;<=","b" => ";<=","a" => 0x1312)

提一点代码风格的建议。

感觉 TCP 有点底层了,julia 可以做,但有些细节解比较难把控,结构体对齐啥的,怕是都得手工来。
julia 应该是直接用的 libuv 给的 API。

  1. 其实前面这种一看就有规律的定义,可以用宏来做。不过算是锦上添花,闲得无聊可以研究一下。
  1. LOAD_PATH 比较暴力,一般用
include("tcp.jl") # 绝对路径、相对路径均可
using .JStruct    # 注意前面的点
  1. 没看懂这个结构体以及 TypeContainer 的用途。需要所有的键值有函数可以用。Dict 已经包含的信息没必要在存一份;keylist 是否多余?
julia> d = Dict(:a => :list, :b => [1 2 3])
Dict{Symbol,Any} with 2 entries:
  :a => :list
  :b => [1 2 3]

julia> keys(d)
Base.KeySet for a Dict{Symbol,Any} with 2 entries. Keys:
  :a
  :b

julia> values(d)
Base.ValueIterator for a Dict{Symbol,Any} with 2 entries. Values:
  :list
  [1 2 3]
  1. 因为 julia 有 dispatch 所以很少看见 if !(typeof(st.typelist[i])<:Tuple),写几个同名函数,输入类型不同,让编译器自己去调对应的函数。

实现 if type 就对函数参数加上对应的类型限制;还可以定义一个参数不加限制(默认为Any)的函数,这个函数调用优先级最低,所以会作为最后的 fallback。也可以用来报错。

想一想,julia 里类型丰富,对于加法 + 如果需要用 typeof 判断类型,那排列组合得多少 if-else。

https://docs.juliacn.com/latest/manual/methods/#定义方法-1

谢大佬指点 那个TypeContainer中的keylist是为了保证字段的有序性,因为我发现julia中的字典访问时是无序的,后面解析时如果没这个keylist 无法保证解析结果的正确性 如果julia里支持有序字典,就不需要这样玩了

那就用具名元组(NamedTuple)。如果你之后不修改输入的数据,元组就非常合适,并且是有序的

Ordered Dicts 有,不过要装包 JuliaCollections/DataStructures.jl

julia> s = (a=:list, b=[1 2 3])
(a = :list, b = [1 2 3])

julia> keys(s)
(:a, :b)

julia> values(s)
(:list, [1 2 3])

julia> typeof(s)
NamedTuple{(:a, :b),Tuple{Symbol,Array{Int64,2}}}

julia> s.a
:list

julia> s.a = :list2
ERROR: setfield! immutable struct of type NamedTuple cannot be changed

可以的 多谢大佬的指点 话说julia有靠谱的文档吗 (除了官方文档)

一般也就推荐官方文档。其他的一些 blog 也可以看一看,书就不太推荐,因为讲的不深和文档差不太多,而且 julia 更新太快,基于 1.0 以上的书还能看看,更老的就用不上了。

不过最新出的 Hands-on Design Patterns and Best Practices with Julia 看书名很吸引人,作者说是在英文社区交流总结得到的,julia 的设计模式总的来说还是偏函数时,不硬上 OOP 应该都挺自由的。

这里面有一些免费的学习资料,不过多是英文的。

例如:ThinkJulia.jl 这本作为官方文档的补充挺不错的,给了不少例子

https://benlauwens.github.io/ThinkJulia.jl/latest/book.html

不行就看源码嘛,julia 推荐多用函数,划分的好的包,一个函数做的工作就是一定的,也不会太长(标准库打脸预定)。