我想要一个可以传递空白修剪字符串的函数,它将返回
0表示错误(不是字符串)1表示ipv4 2表示ipv6 3表示字符串不是ip 。
Ipv6有这些规则:
Ipv6由8组16位十六进制值表示,以冒号(:)为单位分隔
十六进制数字不区分大小写
缩写规则:
1:省略16位值的前导零
2:用双冒号替换一组或多组连续零
wiki示例显示了3种完全相同的方式ipv6:
fe80:0000:0000:0000:0202:b3ff:fe1e:8329
fe80:0:0:0:202:b3ff:fe1e:8329
fe80::202:b3ff:fe1e:8329
我非常肯定你会检查三个ipv4。然后检查字符串是否全部为数字,并且。的计数为数字,最后一次检查只是一个字符串
将在if语句的末尾,所以如果它不是ipv4 / 6及其然后是一个字符串,它返回3
答案 0 :(得分:6)
迈克的解决方案很好,但可以通过多种方式改进。在目前的形式,它没有得到ipv6地址检查,但它很容易修复。对"1050!0!0+0-5@600$300c#326b"
和"1050:0:0:0:5:600:300c:326babcdef"
(将两者都识别为有效地址)和"1050:::600:5:1000::"
(将其识别为字符串)之类的内容进行ipv6检查失败。
以下是改进版本(假设IPv4为十进制数,IPv6假定为十六进制数):
function GetIPType(ip)
local R = {ERROR = 0, IPV4 = 1, IPV6 = 2, STRING = 3}
if type(ip) ~= "string" then return R.ERROR end
-- check for format 1.11.111.111 for ipv4
local chunks = {ip:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$")}
if #chunks == 4 then
for _,v in pairs(chunks) do
if tonumber(v) > 255 then return R.STRING end
end
return R.IPV4
end
-- check for ipv6 format, should be 8 'chunks' of numbers/letters
-- without leading/trailing chars
-- or fewer than 8 chunks, but with only one `::` group
local chunks = {ip:match("^"..(("([a-fA-F0-9]*):"):rep(8):gsub(":$","$")))}
if #chunks == 8
or #chunks < 8 and ip:match('::') and not ip:gsub("::","",1):match('::') then
for _,v in pairs(chunks) do
if #v > 0 and tonumber(v, 16) > 65535 then return R.STRING end
end
return R.IPV6
end
return R.STRING
end
要检查的脚本:
local IPType = {[0] = "Error", "IPv4", "IPv6", "string"}
local ips = {
"128.1.0.1", -- ipv4
"223.255.254.254", -- ipv4
"999.12345.0.0001", -- invalid ipv4
"1050:0:0:0:5:600:300c:326b", -- ipv6
"1050!0!0+0-5@600$300c#326b", -- string
"1050:0:0:0:5:600:300c:326babcdef", -- string
"1050:0000:0000:0000:0005:0600:300c:326b", -- ipv6
"fe80:0000:0000:0000:0202:b3ff:fe1e:8329", -- ipv6
"fe80:0:0:0:202:b3ff:fe1e:8329", -- ipv6
"fe80::202:b3ff:fe1e:8329", -- ipv6
"1050:::600:5:1000::", -- contracted ipv6
"::", -- ipv6
"::1", -- ipv6
"::1::", -- string
"129.garbage.9.1", -- string
"xxx127.0.0.0", -- error
"xxx1050:0000:0000:0000:0005:0600:300c:326b", -- string
129.10 -- error
}
for k,v in pairs(ips) do
print(v, IPType[GetIPType(v)])
end
输出:
128.1.0.1 IPv4
223.255.254.254 IPv4
999.12345.0.0001 string
1050:0:0:0:5:600:300c:326b IPv6
1050!0!0+0-5@600$300c#326b string
1050:0:0:0:5:600:300c:326babcdef string
1050:0000:0000:0000:0005:0600:300c:326b IPv6
fe80:0000:0000:0000:0202:b3ff:fe1e:8329 IPv6
fe80:0:0:0:202:b3ff:fe1e:8329 IPv6
fe80::202:b3ff:fe1e:8329 IPv6
1050:::600:5:1000:: IPv6
:: IPv6
::1 IPv6
::1:: string
129.garbage.9.1 string
xxx127.0.0.0 string
xxx1050:0000:0000:0000:0005:0600:300c:326b string
129.1 Error
在9/6/2018更新,在地址之前/之后添加垃圾处理,并检查合同化的ipv6,允许少于8个组,其中一个空组包含两个连续冒号。
答案 1 :(得分:3)
这似乎是一个非常基本的问题需要解决。我认为这个功能可以满足您的需求...
function GetIPType(ip)
-- must pass in a string value
if ip == nil or type(ip) ~= "string" then
return 0
end
-- check for format 1.11.111.111 for ipv4
local chunks = {ip:match("(%d+)%.(%d+)%.(%d+)%.(%d+)")}
if (#chunks == 4) then
for _,v in pairs(chunks) do
if (tonumber(v) < 0 or tonumber(v) > 255) then
return 0
end
end
return 1
else
return 0
end
-- check for ipv6 format, should be 8 'chunks' of numbers/letters
local _, chunks = ip:gsub("[%a%d]+%:?", "")
if chunks == 8 then
return 2
end
-- if we get here, assume we've been given a random string
return 3
end
使用以下代码对其进行测试:
local IPType = {
[0] = "Error",
[1] = "IPv4",
[2] = "IPv6",
[3] = "string",
}
local ips = {
"128.1.0.1", -- ipv4
"223.255.254.254", -- ipv4
"999.12345.0.0001", -- invalid ipv4
"1050:0:0:0:5:600:300c:326b", -- ipv6
"1050:0000:0000:0000:0005:0600:300c:326b", -- ipv6
"1050:::600:5:1000::", -- contracted ipv6
"129.garbage.9.1", -- string
129.10 -- error
}
for k,v in pairs(ips) do
print(v, IPType[GetIPType(v)])
end
生成此输出:
128.1.0.1 IPv4
223.255.254.254 IPv4
1050:0:0:0:5:600:300c:326b IPv6
1050:0000:0000:0000:0005:0600:300c:326b IPv6
129.garbage.9.1 string
129.1 Error
将来,如果您实际发布了您尝试编写的代码以解决您的特定问题,您将获得更多有用的反馈,并告诉我们您需要帮助的地方。如常见问题所述,SO不是个人代码编写服务。但是,我会给你带来怀疑的好处,因为你看起来很新,这可能会让其他人受益。上面的代码是基本的,所以如果它没有捕获我不知道的边缘测试用例,请随时更新它。
答案 2 :(得分:1)
这似乎可以通过使用正则表达式轻松完成。有很多用于lua的正则表达式库。
但是,如果您不愿意或无法使用它们,我会做这样的事情:
Start in ipv4 state
Take a character until string ends
switch(state)
ipv4:
if it's a dot, check if we loaded at least one number
if it's a number, check if it isn't the 4th in row
if it's anything else, set state to ipv6 and proceed in this state
ipv6:
if it's a ':', check if we didnt exceed maximum number of segments
if it's a number or letter<a;f> check if it isn't 5th in row
in case anything breaks, return 3
end
我不会发布完整的lua代码,因为它看起来像家庭作业/学习练习,完整的答案会伤害你,而不是帮助你。
答案 3 :(得分:0)
有趣的是,上述答案都没有考虑到原始问题的测试示例,因为使用它们,所有上述检查都会失败(因为#3):
fe80:0000:0000:0000:0202:b3ff:fe1e:8329
fe80:0:0:0:202:b3ff:fe1e:8329
fe80::202:b3ff:fe1e:8329 (!)
IPv6表示规则说:
一个或多个连续的零值组可以使用两个连续的冒号(:),1替换为一个空组,但替换只能在地址中应用一次,因为多次出现会创建模棱两可的表现。 https://en.wikipedia.org/wiki/IPv6_address#Representation
由于Lua模式不支持Alternation,因此无法使用单一模式检查IPv6。您可能会看到David M. Syzdek对IPv6 Regex的复杂性做出回答:https://stackoverflow.com/a/17871737/1895269
仍然,更符合标准的方法是Paul Kulchenko的回答如下:
function GetIPType(ip)
local R = {ERROR = 0, IPV4 = 1, IPV6 = 2, STRING = 3}
if type(ip) ~= "string" then return R.ERROR end
-- check for format 1.11.111.111 for ipv4
local chunks = { ip:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$") }
if (#chunks == 4) then
for _,v in pairs(chunks) do
if tonumber(v) > 255 then return R.STRING end
end
return R.IPV4
end
-- check for ipv6 format, should be max 8 'chunks' of numbers/letters
local addr = ip:match("^([a-fA-F0-9:]+)$")
if addr ~= nil and #addr > 1 then
-- address part
local nc, dc = 0, false -- chunk count, double colon
for chunk, colons in addr:gmatch("([^:]*)(:*)") do
if nc > (dc and 7 or 8) then return R.STRING end -- max allowed chunks
if #chunk > 0 and tonumber(chunk, 16) > 65535 then
return R.STRING
end
if #colons > 0 then
-- max consecutive colons allowed: 2
if #colons > 2 then return R.STRING end
-- double colon shall appear only once
if #colons == 2 and dc == true then return R.STRING end
if #colons == 2 and dc == false then dc = true end
end
nc = nc + 1
end
return R.IPV6
end
return R.STRING
end
要检查的脚本:
local IPType = {[0] = "Error", "IPv4", "IPv6", "string"}
local ips = {
"128.1.0.1", -- ipv4
"223.255.254.254", -- ipv4
"999.12345.0.0001", -- invalid ipv4
"1050:0:0:0:5:600:300c:326b", -- ipv6
"1050!0!0+0-5@600$300c#326b", -- string
"1050:0:0:0:5:600:300c:326babcdef", -- string
"1050:0000:0000:0000:0005:0600:300c:326b", -- ipv6
"1050:::600:5:1000::", -- contracted ipv6 (invalid)
"fe80::202:b3ff:fe1e:8329", -- shortened ipv6
"fe80::202:b3ff::fe1e:8329", -- shortened ipv6 (invalid)
"fe80:0000:0000:0000:0202:b3ff:fe1e:8329:abcd", -- too many groups
"::1", -- valid IPv6
"::", -- valid IPv6
":", -- string
"129.garbage.9.1", -- string
129.10 -- error
}
for k,v in pairs(ips) do
print(v, IPType[GetIPType(v)])
end
输出:
128.1.0.1 IPv4
223.255.254.254 IPv4
999.12345.0.0001 string
1050:0:0:0:5:600:300c:326b IPv6
1050!0!0+0-5@600$300c#326b string
1050:0:0:0:5:600:300c:326babcdef string
1050:0000:0000:0000:0005:0600:300c:326b IPv6
1050:::600:5:1000:: string
fe80::202:b3ff:fe1e:8329 IPv6
fe80::202:b3ff::fe1e:8329 string
fe80:0000:0000:0000:0202:b3ff:fe1e:8329:abcd string
::1 IPv6
:: IPv6
: string
129.garbage.9.1 string
129.1 Error
答案 4 :(得分:0)
由于Lua's regular expressions的表达能力不足,因此必须继续使用迭代算法。
我建议您检查一下我在意大利维基百科上发布的one(已fully tested):
local R = {ERROR = 0, IPV4 = 1, IPV6 = 2, STRING = 3}
function is_ipv4(str)
local s = str:gsub("/[0-9]$", ""):gsub("/[12][0-9]$", ""):gsub("/[3][0-2]$", "")
if not s:find("^%d+%.%d+%.%d+%.%d+$") then
return nil
end
for substr in s:gmatch("(%d+)") do
if not substr:find("^[1-9]?[0-9]$")
and not substr:find("^1[0-9][0-9]$")
and not substr:find( "^2[0-4][0-9]$")
and not substr:find("^25[0-5]$") then
return nil
end
end
return R.IPV4
end
function is_ipv6(str)
local s = str
if not (s:find("^%w+:%w+:%w+:%w+:%w+:%w+:%w+:%w+$") -- there are exactly seven ":"
or (s:find("^%w*:%w*:%w*:?%w*:?%w*:?%w*:?%w*$") -- otherwise there are two to six sei ":"
and s:find("::"))) -- and there must be the substring "::"
or s:find("::.*::") -- but there cannot be neither two substrings "::"
or s:find(":::") then -- nor a substring ":::"
return nil
end
for substr in s:gmatch("(%w+)") do
if not substr:find("^[0-9A-Fa-f][0-9A-Fa-f]?[0-9A-Fa-f]?[0-9A-Fa-f]?$") then
return nil
end
end
return R.IPV6
end
function ip_type(str)
if type(str) ~= "string" then
return R.ERROR
else
return is_ipv4(str) or is_ipv6(str) or R.STRING
end
end
编辑:我按照OP的要求更改了ip_type()
函数的输出。