lua new

table.new(narray, nhash) 两个参数分别代表table里是array还是hash的

table.new(10, 0) 或者 table.new(0, 10) 这样的,后者是 hash 性质的 table

lua table可以同时拥有数组部分和哈希部分。在物理上,数组部分和哈希部分也是在 table 内部分开存储的。

比如

table.new(100, 200) 也是完全合法的。

table.new(narr, nrec) 接口是标准 Lua C API 函数 lua_createtable() 的 Lua 版本:

http://www.lua.org/manual/5.1/ manual.html#lua_createtable

另外,不同于使用了 lua_createtable() 的 lua_CFunction,table.new() 是可以被 JIT 编译的。

table.new()一般用于给 lua table 预分配空间。

否则 table 会在插入新元素时自增长,而自增长是高代价操作( 因为需要重新分配空间、重新 hash,以及拷贝数据)

ngx lua正则

ngx_lua模块中正则表达式相关的api

在Lua模块中,通过正则表达式处理字符串的相关API,主要有:

ngx.re.match

语法:captures, err = ngx.re.match(subject, regex, options?, ctx?, res_table?)

只有第一次匹配的结果被返回,如果没有匹配,则返回nil;或者匹配过程中出现错误时,也会返回nil,此时错误信息会被保存在err中。

当匹配的字符串找到时,一个lua table captures会被返回,captures[0]中保存的就是匹配到的字串,captures[1]保存的是用括号括起来的第一个子模式的结果,captures[2]保存的是第二个子模式的结果,依次类似。

local m, err = ngx.re.match("hello, 1234", "[0-9]+")if m then-- m[0] == "1234"elseif err thenngx.log(ngx.ERR, "error: ", err)returnendngx.say("match not found")end

上面例子中,匹配的字符串是1234,因此m[0] == "1234",但是没有用括号括起来的子模式,因此,m[1],m[2]等均为nil。

local m, err = ngx.re.match("hello, 1234", "([0-9])[0-9]+")-- m[0] == "1234"-- m[1] == "1"

命名方式的捕获,从v0.7.14版本后开始支持,如下所示:

local m, err = ngx.re.match("hello, 1234", "([0-9])(?<remaining>[0-9]+)")-- m[0] == "1234"-- m[1] == "1"-- m[2] == "234"-- m["remaining"] == "234"
local m, err = ngx.re.match("hello, world", "(world)|(hello)|(?<named>howdy)")-- m[0] == "hello"-- m[1] == nil-- m[2] == "hello"-- m[3] == nil-- m["named"] == nil

为什么m[1]等于nil?因为在给定的模式串中,最先匹配的是hello,因此,其他子模式在找到的匹配串中查找不到对应的匹配串,因此,除了hello子模式外,其他的子模式的匹配结果都是nil。一定要记住,是谁最先被匹配的。

options选项可以是下面的取值的组合:

a 锚定模式,只从头开始匹配。

d DFA模式,或者称最长字符串匹配语义,需要PCRE 6.0+支持。

D 允许重复的命名的子模式,该选项需要PCRE 8.12+支持,例如

local m = ngx.re.match("hello, world","(?<named>\w+), (?<named>\w+)","D")

-- m["named"] == {"hello", "world"}

i 大小写不敏感模式。

j 启用PCRE JIT编译, 需要PCRE 8.21+ 支持,并且必须在编译时加上选项--enable-jit,为了达到最佳性能,该选项总是应该和'o'选项搭配使用。

J 启用PCRE Javascript的兼容模式,需要PCRE 8.12+ 支持。

m 多行模式。

o 一次编译模式,启用worker-process级别的编译正则表达式的缓存。

s 单行模式。

u UTF-8模式. 该选项需要在编译PCRE库时加上--enable-utf8 选项。

U 与"u" 选项类似,但是该项选禁止PCRE对subject字符串UTF-8有效性的检查。

x 扩展模式。

两个例子:

local m, err = ngx.re.match("hello, world", "HEL LO", "ix")-- m[0] == "hello"local m, err = ngx.re.match("hello, 美好生活", "HELLO, (.{2})", "iu")-- m[0] == "hello, 美好"-- m[1] == "美好"

第四个可选参数ctx可以传入一个lua table,传入的lua table可以是一个空表,也可以是包含pos字段的lua table。如果传入的是一个空的lua table,那么,ngx.re.match将会从subject字符串的起始位置开始匹配查找,查找到匹配串后,修改pos的值为匹配字符串的下一个位置的值,并将pos的值保存到ctx中,如果匹配失败,那么pos的值保持不变;如果传入的是一个非空的lua table,即指定了pos的初值,那么ngx.re.match将会从指定的pos的位置开始进行匹配,如果匹配成功了,修改pos的值为匹配字符串的下一个位置的值,并将pos的值保存到ctx中,如果匹配失败,那么pos的值保持不变。

local ctx = {}local m, err = ngx.re.match("1234, hello", "[0-9]+", "", ctx)-- m[0] = "1234"-- ctx.pos == 5local ctx = { pos = 2 }local m, err = ngx.re.match("1234, hello", "[0-9]+", "", ctx)-- m[0] = "34"-- ctx.pos == 5 

如果需要传入ctx参数,但并不需要第三个可选参数options时,第三个参数也不能简单去掉,这时需要传入一个空的字符串作为第三个参数的值。

第四个可选参数还不是很熟悉,暂且留空。

ngx.re.find

语法:from, to, err = ngx.re.find(subject, regex, options?, ctx?, nth?)

该方法与ngx.re.match方法基本类似,不同的地方在于ngx.re.find返回的是匹配的字串的起始位置索引和结束位置索引,如果没有匹配成功,那么将会返回两个nil,如果匹配出错,还会返回错误信息到err中。例子:

local s = "hello, 1234"local from, to, err = ngx.re.find(s, "([0-9]+)", "jo")if from thenngx.say("from: ", from)ngx.say("to: ", to)ngx.say("matched: ", string.sub(s, from, to))elseif err thenngx.say("error: ", err)returnendngx.say("not matched!")end输出结果:from: 8to: 11matched: 1234

该方法相比ngx.re.match,不会创建新的lua字符串,也不会创建新的lua Table,因此,该方法比ngx.re.match更加高效,因此,在可以使用ngx.re.find的地方应该尽量使用。

第五个参数可以指定返回第几个子模式串的起始位置和结束位置的索引值,默认值是0,此时将会返回匹配的整个字串;如果nth等于1,那么将返回第一个子模式串的始末位置的索引值;如果nth等于2,那么将返回第二个子模式串的始末位置的索引值,依次类推。如果nth指定的子模式没有匹配成功,那么将会返回两个nil。

local str = "hello, 1234"local from, to = ngx.re.find(str, "([0-9])([0-9]+)", "jo", nil, 2)if from thenngx.say("matched 2nd submatch: ", string.sub(str, from, to)) -- yields "234"end

ngx.re.gmatch

语法:iterator, err = ngx.re.gmatch(subject, regex, options?)

与ngx.re.match相似,区别在于该方法返回的是一个Lua的迭代器,这样就可以通过迭代器遍历所有匹配的结果。

如果匹配失败,将会返回nil,如果匹配出现错误,那么还会返回错误信息到err中。

local iterator, err = ngx.re.gmatch("hello, world!", "([a-z]+)", "i")if not iterator thenngx.log(ngx.ERR, "error: ", err)returnendlocal mm, err = iterator() -- m[0] == m[1] == "hello"if err thenngx.log(ngx.ERR, "error: ", err)returnendm, err = iterator() -- m[0] == m[1] == "world"if err thenngx.log(ngx.ERR, "error: ", err)returnendm, err = iterator() -- m == nilif err thenngx.log(ngx.ERR, "error: ", err)returnend

更多情时候,只需要把迭代过程放入一个while循环中即可:

local it, err = ngx.re.gmatch("hello, world!", "([a-z]+)", "i")if not it thenngx.log(ngx.ERR, "error: ", err)returnendwhile true dolocal m, err = it()if err thenngx.log(ngx.ERR, "error: ", err)returnendif not m then-- no match found (any more)breakend-- found a matchngx.say(m[0])ngx.say(m[1])end

options选项的使用与ngx.re.match中的options选项的用法是一样的。

ngx.re.gmatch返回的迭代器只能在一个请求所在的环境中使用,就是说,我们不能把返回的迭代器赋值给持久存在的命名空间(比如一个Lua Packet)中的某一个变量。

ngx.re.sub

语法:newstr, n, err = ngx.re.sub(subject, regex, replace, options?)

该方法主要实现匹配字符串的替换,会用replace替换匹配的字串,replace可以是纯字符串,也可以是使用$0, $1等子模式串的形式,ngx.re.sub返回进行替换后的完整的字符串,同时返回替换的总个数;options选项,与ngx.re.match中的options选项是一样的。

local newstr, n, err = ngx.re.sub("hello, 1234", "([0-9])[0-9]", "[$0][$1]")if newstr then-- newstr == "hello, [12][1]34"-- n == 1elsengx.log(ngx.ERR, "error: ", err)returnend

在上面例子中,$0表示整个匹配的子串,$1表示第一个子模式匹配的字串,以此类推。

可以用大括号{}将相应的0,1,2...括起来,以区分一般的数字:

local newstr, n, err = ngx.re.sub("hello, 1234", "[0-9]", "${0}00")-- newstr == "hello, 100234"-- n == 1

如果想在replace字符串中显示$符号,可以用$进行转义(不要用反斜杠\$对美元符号进行转义,这种方法不会得到期望的结果):

local newstr, n, err = ngx.re.sub("hello, 1234", "[0-9]", "$$")-- newstr == "hello, $234"-- n == 1

如果replace是一个函数,那么函数的参数是一个"match table", 而这个"match table"与ngx.re.match中的返回值captures是一样的,replace这个函数根据"match table"产生用于替换的字符串。

local func = function (m)return "[" .. m[0] .. "][" .. m[1] .. "]"endlocal newstr, n, err = ngx.re.sub("hello, 1234", "( [0-9] ) [0-9]", func, "x")-- newstr == "hello, [12][1]34"-- n == 1

注意:通过函数形式返回的替换字符串中的美元符号$不再是特殊字符,而只是被看作一个普通字符。

ngx.re.gsub

语法:newstr, n, err = ngx.re.gsub(subject, regex, replace, options?)

该方法与ngx.re.sub是类似的,但是该方法进行的是全局替换。

两个例子:

local newstr, n, err = ngx.re.gsub("hello, world", "([a-z])[a-z]+", "[$0,$1]", "i")if newstr then-- newstr == "[hello,h], [world,w]"-- n == 2elsengx.log(ngx.ERR, "error: ", err)returnend
local func = function (m)return "[" .. m[0] .. "," .. m[1] .. "]"endlocal newstr, n, err = ngx.re.gsub("hello, world", "([a-z])[a-z]+", func, "i")-- newstr == "[hello,h], [world,w]"-- n == 2

ngx_lua如果是0.9.2以上版本,建议正则过滤函数改为ngx.re.find,匹配效率会提高三倍左右。

项目实战

项目中body_filter阶段代码实例如下:

local resp_body = ngx.arg[1] --获取响应体
local eof = ngx.arg[2]
local ctx_log = {} --日志table
local regex = [[You have an error in your SQL syntax]] --匹配的敏感内容
local m = ngx.re.match(resp_body, regex, 'jio') --对响应体做正则匹配
if m then --如果匹配到敏感信息 ctx_log.rule_match = m[0] --将匹配内容写入日志ctx_log.Request_line = ngx.var.request --记录请求URL,包括GET参数 ctx_log.Request_headers = ngx.req.get_headers() --记录请求头部 ngx.ctx.log = ctx_log --日志赋值给跨阶段的ngx.ctx.log
end

项目中log阶段代码实例如下:

local logger = require "socket" --加载logger socket库
local cjson = require "cjson.safe" --加载cjson库if not logger.initted() then --初始化logger local ok,err = logger.init{ host = "127.0.0.1", --splunk IP port = 8888, --splunk端口 sock_type = "tcp", --日志socket类型 flush_limit = 1, }if not ok then --初始化失败处理 ngx.log(ngx.ERR,"failed to initialize the logger: ",err) returnend
endlocal log = ngx.ctx.log --接收ngx.ctx.log跨阶段传过来的日志信息
if type(log) == "table" then --判断日志不为空则记录local bytes, err = logger.log(cjson.encode(log) .. "\r\n") if err thenngx.log(ngx.ERR, "failed to log message: ", err) end
end

Splunk需要注意的地方:需要编辑props.conf以免在日志过多的时候Splunk自动把多行Json格式日志合并vim /opt/splunk/etc/system/local/props.conf。加入以下内容,[_json] #这个是sourcetype SHOULD_LINEMERGE = false #告诉Splunk不自动合并行。

ngx lua变量

Lua | NGINX

lua编程

使用string.match 和正则实现对字符串的去除两端空格;  

Str = string.match(Str,"%s*(.-)%s*$");

使用string.match 和正则实现对字母、数字、汉字外的字符进行判断;

Str2 = string.match(Str2,"^[A-Z-a-z-0-9-\128-\254]+$");

tab = {"a", "c", "d", "b"}print(table.concat(tab))   --> 输出结果: acdbprint(table.concat(tab, " @@ ", 2))  --> 输出结果: c @@ d @@ bprint( table.concat(tab, " - ", 2, 3))  --> 输出结果: c - dprint(table.concat(tab, " ## \n", 2, 4))   --> 输出结果:--> c ##--> d ##--> b
fruits = {"banana","orange","apple"}-- 在末尾插入table.insert(fruits,"mango")print("索引为 4 的元素为 ",fruits[4])-- 在索引为 2 的键处插入table.insert(fruits,2,"grapes")print("索引为 2 的元素为 ",fruits[2])print("最后一个元素为 ",fruits[5])table.remove(fruits)print("移除后最后一个元素为 ",fruits[5])

__index 元方法

通过键来访问 table 的时候,如果这个键没有值,那么Lua就会寻找该table的metatable中的__index 键。如果__index包含一个表格,Lua会在表格中查找相应的键。

$ luaLua 5.3.0  Copyright (C) 1994-2015 Lua.org, PUC-Rio> other = { foo = 3 }> t = setmetatable({}, { __index = other })> t.foo3> t.barnil

如果__index包含一个函数的话,lua就会调用那个函数,table和键会作为参数传递给函数。

__index 元方法也就是function查看表中元素是否存在,如果不存在,返回结果为 nil;如果存在则由 __index 返回结果。

mytable = setmetatable({key1 = "value1"}, {__index = function(mytable, key)if key == "key2" thenreturn "metatablevalue"elsereturn nilendend})print(mytable.key1,mytable.key2)实例输出结果为:value1    metatablevalue
  • mytable 表赋值为 {key1 = "value1"}。mytable 设置了元表,元方法为 __index。
  • 在mytable表中查找 key1,如果找到,返回该元素,找不到则继续。
  • 在mytable表中查找 key2,如果找到,返回 metatablevalue,找不到则继续。
  • 判断元表有没有__index方法,如果__index方法是一个函数,则调用该函数。
  • 元方法function中查看是否传入 "key2" 键的参数(mytable.key2已设置),如果传入 "key2" 参数返回 "metatablevalue",否则返回 mytable 对应的键值。

上代码简单写成:

mytable = setmetatable({key1 = "value1"}, { __index = { key2 = "metatablevalue" } })print(mytable.key1,mytable.key2)

__newindex 元方法

__newindex 元方法用来对表更新,__index则用来对表访问 。

给表的一个缺少的索引赋值,解释器就会查找__newindex 元方法:如果存在则调用这个函数而不进行赋值操作。

mymetatable = {}mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable })print(mytable.key1)mytable.newkey = "新值2"print(mytable.newkey,mymetatable.newkey)mytable.key1 = "新值1"print(mytable.key1,mymetatable.key1)以上实例执行输出结果为:value1 nil    新值2 新值1    nil

在对新索引键(newkey)赋值时(mytable.newkey = "新值2"),会调用元方法,而不进行赋值。而如果对已存在的索引键(key1),则会进行赋值,而不调用元方法 __newindex。

以下实例使用了 rawset 函数来更新表:

实例

mytable = setmetatable({key1 = "value1"}, {__newindex = function(mytable, key, value)rawset(mytable, key, "\""..value.."\"")end})mytable.key1 = "new value"mytable.key2 = 4print(mytable.key1,mytable.key2)以上实例执行输出结果为:new value    "4"

为表添加操作符

以下实例演示了两表相加操作:

实例

-- 计算表中最大值,table.maxn在Lua5.2以上版本中已无法使用

-- 自定义计算表中最大键值函数 table_maxn,即计算表的元素个数

function table_maxn(t)local mn = 0for k, v in pairs(t) doif mn < k thenmn = kendendreturn mnend

-- 两表相加操作

mytable = setmetatable({ 1, 2, 3 }, {__add = function(mytable, newtable)for i = 1, table_maxn(newtable) dotable.insert(mytable, table_maxn(mytable)+1,newtable[i])endreturn mytableend})secondtable = {4,5,6}mytable = mytable + secondtablefor k,v in ipairs(mytable) doprint(k,v)end以上实例执行输出结果为:1    1 2    2 3    3 4    4 5    5 6    6

模式

描述

__add

对应的运算符 '+'.

__sub

对应的运算符 '-'.

__mul

对应的运算符 '*'.

__div

对应的运算符 '/'.

__mod

对应的运算符 '%'.

__unm

对应的运算符 '-'.

__concat

对应的运算符 '..'.

__eq

对应的运算符 '=='.

__lt

对应的运算符 '<'.

__le

对应的运算符 '<='.

__call 元方法

__call 元方法在 Lua 调用一个值时调用。以下实例演示了计算表中元素的和:

-- 定义元方法__call

mytable = setmetatable({10}, {__call = function(mytable, newtable)sum = 0for i = 1, table_maxn(mytable) dosum = sum + mytable[i]endfor i = 1, table_maxn(newtable) dosum = sum + newtable[i]endreturn sumend})newtable = {10,20,30}print(mytable(newtable))以上实例执行输出结果为:70

__tostring 元方法

__tostring 元方法用于修改表的输出行为。以下实例我们自定义了表的输出内容:

实例

mytable = setmetatable({ 10, 20, 30 }, {__tostring = function(mytable)sum = 0for k, v in pairs(mytable) dosum = sum + vendreturn "表所有元素的和为 " .. sumend})print(mytable)以上实例执行输出结果为:表所有元素的和为 60

Lua的继承(利用setmetatable)

local Animal = {age = 3,gender = "male"} --定义一个Animal的表function Animal:extend() --定义表中的一个extend方法(继承机制的核心就是下面四行代码) local obj = obj or {} --if obj not nil return obj,else return {}setmetatable(obj, self) --set self as obj's metatable 把Animal表自己作为元表放入obj表中 self.__index = self --索引__index是一个特殊的机制,只有设置了索引值,才能在子类调用父类方法时,能找到self(也就是Animal表)中的方法 return obj --返回带有元表的obj
endfunction Animal:run() --定义父类的一个方法,作为动物,都可以跑的公共函数 CCLuaLog("run is my gift~!")
endreturn Animal
local Horse = require("GameObject.Animal"):extend() --获取带有元表的obj function Horse:eat() --子类的方法 CCLuaLog("eat grass..."..self.age)
end return Horse
local pony = require("GameObject.Horse") pony:run()
pony:eat()

ngx lua操作共享内存

> ngx.shared.DICT.get

语法:value, flags = ngx.shared.DICT:get(key)

获取共享内存上key对应的值。如果key不存在,或者key已经过期,将会返回nil;如果出现错误,那么将会返回nil以及错误信息。

local cats = ngx.shared.cats

local value, flags = cats.get(cats, "Marry")

等价于

local cats = ngx.shared.cats

local value, flags = cats:get("Marry")

返回列表中的flags,是在ngx.shared.DICT.set方法中设置的值,默认值为0. 如果设置的flags为0,那么在这里flags的值将不会被返回。

> ngx.shared.DICT.get_stale

语法:value, flags, stale = ngx.shared.DICT:get_stale(key)

与get方法类似,区别在于该方法对于过期的key也会返回,第三个返回参数表明返回的key的值是否已经过期,true表示过期,false表示没有过期。

> ngx.shared.DICT.set

语法:success, err, forcible = ngx.shared.DICT:set(key, value, exptime?, flags?)

“无条件”地往共享内存上插入key-value对,这里讲的“无条件”指的是不管待插入的共享内存上是否已经存在相同的key。三个返回值的含义:

success:成功插入为true,插入失败为false

err:操作失败时的错误信息,可能类似"no memory"

forcible:true表明需要通过强制删除(LRU算法)共享内存上其他字典项来实现插入,false表明没有删除共享内存上的字典项来实现插入。

第三个参数exptime表明key的有效期时间,单位是秒(s),默认值为0,表明永远不会过期;flags参数是一个用户标志值,会在调用get方法时同时获取得到。

local cats = ngx.shared.cats

local succ, err, forcible = cats.set(cats, "Marry", "it is a nice cat!")

等价于

local cats = ngx.shared.cats

local succ, err, forcible = cats:set("Marry", "it is a nice cat!")

> ngx.shared.DICT.safe_set

语法:ok, err = ngx.shared.DICT:safe_set(key, value, exptime?, flags?)

与set方法类似,区别在于不会在共享内存用完的情况下,通过强制删除(LRU算法)的方法实现插入。如果内存不足,会直接返回nil和err信息"no memory"

注意:

set和safe_set共同点是:如果待插入的key已经存在,那么key对应的原来的值会被新的value覆盖!

> ngx.shared.DICT.add

语法:success, err, forcible = ngx.shared.DICT:add(key, value, exptime?, flags?)

与set方法类似,与set方法区别在于不会插入重复的键(可以简单认为add方法是set方法的一个子方法),如果待插入的key已经存在,将会返回nil和和err="exists"

> ngx.shared.DICT.safe_add

语法:ok, err = ngx.shared.DICT:safe_add(key, value, exptime?, flags?)

与safe_set方法类似,区别在于不会插入重复的键(可以简单认为safe_add方法是safe_set方法的一个子方法),如果待插入的key已经存在,将会返回nil和和err="exists"

> ngx.shared.DICT.replace

语法:success, err, forcible = ngx.shared.DICT:replace(key, value, exptime?, flags?)

与set方法类似,区别在于只对已经存在的key进行操作(可以简单认为replace方法是set方法的一个子方法),如果待插入的key在字典上不存在,将会返回nil和错误信息"not found"

> ngx.shared.DICT.delete

语法:ngx.shared.DICT:delete(key)

无条件删除指定的key-value对,其等价于

ngx.shared.DICT:set(key, nil)

> ngx.shared.DICT.incr

语法:newval, err = ngx.shared.DICT:incr(key, value)

对key对应的值进行增量操作,增量值是value,其中value的值可以是一个正数,0,也可以是一个负数。value必须是一个Lua类型中的number类型,否则将会返回nil和"not a number";key必须是一个已经存在于共享内存中的key,否则将会返回nil和"not found".

> ngx.shared.DICT.flush_all

语法:ngx.shared.DICT:flush_all()

清除字典上的所有字段,但不会真正释放掉字段所占用的内存,而仅仅是将每个字段标志为过期。

> ngx.shared.DICT.flush_expired

语法:flushed = ngx.shared.DICT:flush_expired(max_count?)

清除字典上过期的字段,max_count表明上限值,如果为0或者没有给出,表明需要清除所有过期的字段,返回值flushed是实际删除掉的过期字段的数目。

注意:

与flush_all方法的区别在于,该方法将会释放掉过期字段所占用的内存。

> ngx.shared.DICT.get_keys

语法:keys = ngx.shared.DICT:get_keys(max_count?)

从字典上获取字段列表,个数为max_count,如果为0或没有给出,表明不限定个数。默认值是1024个

强烈建议在调用该方法时,指定一个max_count参数,因为在keys数量很大的情况下,如果不指定max_count的值,可能会导致字典被锁定,从而阻塞试图访问字典的worker进程。

nginx lua编程要点,看完肯定会提高你的编码能力相关推荐

  1. 不要说零基础学不好UG编程,看完你就有方向了

    零基础怎么学UG编程,看完你就懂了 不要说零基础学不好UG编程,看完你就有方向了 众所周知UG编程师是一份工资待遇很不错的工作,而且还特别轻松 怎么样才能成为UG编程师呢? 打个比方,以前有很多人都在 ...

  2. 三万字的java I/O流基础总结看完肯定能把女朋友教会

    目录: 1.File类详解 2.I/O流概述 3.字节输出流(OutputStream) 4.字节输入流(InputStream) 5. 字符流概述 6.字符输入流(Reader) 7.字符输出流(W ...

  3. 大一女生废话编程爆火!懂不懂编程的看完都拴Q了

    杨净 丰色 发自 凹非寺 量子位 | 公众号 QbitAI 她的日更作业,竟让网友直呼:中国计算机界的神! 短短两个星期的时间里,这个女大学生实力吸引了40万+粉丝,超260万的点赞. 而平日里底下的 ...

  4. 上位机plc编程入门_plc编程入门-看完你就懂了

    1.PLC的发展历程 在工业生产过程中,大量的开关量顺序控制,它按照逻辑条件进行顺序动作,并按照逻辑关系进行连锁保护动作的控制,及大量离散量的数据采集.传统上,这些功能是通过气动或电气控制系统来实现的 ...

  5. Linux开机自启动jar包详细操作,认真看完肯定可以完成

    Linux开机自启动jar包脚本 有xshll和xftp的可以直接在Windows系统下创建一个txt文件贴上以下代码 #!/bin/sh export JAVA_HOME=你Linux系统下的jav ...

  6. 科研工具篇|看完之后能提高你80%的科研工作效率

    荀子在<劝学篇>中讲到,"君子性非异也,善假于物也".在生活中,善用各种工具能够极大的提高我们的效率,在科研工作中亦是如此.今天给大家介绍在科学研究和论文写作自己常用到 ...

  7. 看完就懂的《H264编码原理及框图》

    H264编码原理及框图 ------------ 分析H264原理: H264是新一代的编码标准,以高压缩高质量和支持多重网络的流媒体著称,在编码上,主要是以下理解:参照一段时间内图像的统计结果表明, ...

  8. 吐血推荐 ▏看完这个我才知道原来PHP应该这样学!大牛的成功是可以复制的(福利派送)

    吐血推荐 ▏看完这个我才知道原来PHP应该这样学!大牛的成功是可以复制的(福利派送) PHP以其简单易学的特点,以及敏捷开发的优势,从一个几乎不为人知的开源项目,慢慢成长为技术人员首选的动态Web设计 ...

  9. 看完源码记不住,是我记性太差了吗?

    都说大厂面试必问源码,尤其是现在最流行的Java 开发技术--Spring的源码.可很多人看完Spring源码记不住,是记性太差了吗? 当然不是!是因为你没有掌握学习源码的技巧. 看完源码的我- 前段 ...

最新文章

  1. LINUX - pthread_detach()与pthread_join()
  2. arcgis下的python编程-面向ArcGIS的Python脚本编程
  3. Java String关于replaceall函数转义字符的一个小贴士
  4. linux python连接oracle数据库_Linux下通过python访问MySQL、Oracle、SQL Server数据库的方法...
  5. nvml.dll 英伟达公司提供的动态库用途
  6. c#中控件重绘(放大缩小移动隐藏恢复后不消失)实例
  7. oracle天数加个随机数,如何给一个表某列加上指定的随机数
  8. TCP协议-相关面试题
  9. PyCharm安装与配置,python的Hello World
  10. Tensorflow 迁移学习 识别中国军网、中国军视网Logo水印
  11. 【文件系统】NTFS、FAT32、exFAT
  12. lenovo Win10 安装 Androd Studio 新版本编程开发的软件
  13. Java将每半年发布一个版本
  14. 连发生成工具_Win10菜单习惯吗?快看吧,Win10快速切换成Win7菜单就这么简单
  15. 您无权查看或编辑目前的权限设置;但是,您可以取得所有权或更改审核设置
  16. Sprintboot 解压Zip文件,ZipEntry的zipEntry.getSize()为-1的问题
  17. 阿里云DataV结合LayUI的一次实战
  18. 怎样配置 Docker IPv6 ?
  19. 19个免费的UI界面设计工具及资源
  20. APICloud入门初体验

热门文章

  1. 致远SPM之智慧印章管理解决方案
  2. 骨传导蓝牙耳机好用吗,五款好用的骨传导耳机推荐
  3. 庆祝自己博客重新回到ITeye
  4. “抓住神经猫”的2D游戏创作
  5. Knife4j官网(内含源代码)
  6. 在Oracle中巧用column_value解决游标传入多值问题
  7. 用CSS添加鼠标样式
  8. SQL Server 事务日志常用SQL
  9. doc 2 pdf java_java doc转pdf
  10. Mysql 添加外键的两种方式