function AnsiToUtf8(s) local r, b = '' for i = 1, s and s:len() or 0 do b = s:byte(i) if b < 128 then r = r..string.char(b) else if b > 239 then r = r..'\209'..string.char(b - 112) elseif b > 191 then r = r..'\208'..string.char(b - 48) elseif ansi_decode[b] then r = r..ansi_decode[b] else r = r..'_' end end end return r end
function Utf8ToAnsi(s) local a, j, r, b = 0, 0, '' for i = 1, s and s:len() or 0 do b = s:byte(i) if b < 128 then if nmdc[b] then r = r..nmdc[b] else r = r..string.char(b) end elseif a == 2 then a, j = a - 1, b elseif a == 1 then a, r = a - 1, r..utf8_decode[j][b] elseif b == 226 then a = 2 elseif b == 194 or b == 208 or b == 209 or b == 210 then j, a = b, 1 else r = r..'_' end end return r end
Автор: Setuper 13.9.2008, 18:06
Функция приведения к нижнему регистру:
Код
function String2Lower(s) local r, b = '' s = s:lower() for i = 1, s:len() do b = s:byte(i) if b > 191 and b < 224 then b = b + 32 elseif b == 168 or b == 184 then b = 229 end r = r.._G.string.char(b) end return r end
Функция приведения к верхнему регистру:
Код
function String2Upper(s) local r, b = '' s = s:upper() for i = 1, s:len() do b = s:byte(i) if b > 223 then b = b - 32 elseif b == 168 or b == 184 then b = 197 end r = r.._G.string.char(b) end return r end
Более оптимальные по выполнению функции:
Код
String2Lower = function(s) for i = 192, 223 do s = s:gsub(_G.string.char(i), _G.string.char(i + 32)) end s = s:gsub(_G.string.char(168), _G.string.char(184)) return s:lower() end
Код
String2Upper = function(s) for i = 224, 255 do s = s:gsub(_G.string.char(i), _G.string.char(i - 32)) end s = s:gsub(_G.string.char(184), _G.string.char(168)) return s:upper() end
Автор: Setuper 14.9.2008, 11:36
Функции форматирования шары:
Код
function GetNormalShare(s) s = tonumber(s) or 0 if s >= 1125899906842624 then return (math.floor(1000 * s / 1125899906842624) / 1000).." ПБ" elseif s >= 1099511627776 then return (math.floor(1000 * s / 1099511627776) / 1000).." TБ" elseif s >= 1073741824 then return (math.floor(1000 * s / 1073741824) / 1000).." ГБ" elseif s >= 1048576 then return (math.floor(1000 * s / 1048576) / 1000).." МБ" elseif s >= 1024 then return (math.floor(1000 * s / 1024) / 1000).." КБ" else return s.." Б" end end
Код
function GetLongShare(s) s = tonumber(s) or 0 local r = '' if s >= 1125899906842624 then r = r ~= '' and r..(" %s ПБ"):format(math.floor(s / 1125899906842624)) or r..math.floor(s / 1125899906842624).." ПБ" s = math.fmod(s, 1125899906842624) end if s >= 1099511627776 then r = r ~= '' and r..(" %s ТБ"):format(math.floor(s / 1099511627776)) or r..math.floor(s / 1099511627776).." ТБ" s = math.fmod(s, 1099511627776) end if s >= 1073741824 then r = r ~= '' and r..(" %s ГБ"):format(math.floor(s / 1073741824)) or r..math.floor(s / 1073741824).." ГБ" s = math.fmod(s, 1073741824) end if s >= 1048576 then r = r ~= '' and r..(" %s МБ"):format(math.floor(s / 1048576)) or r..math.floor(s / 1048576).." МБ" s = math.fmod(s, 1048576) end if s >= 1024 then r = r ~= '' and r..(" %s КБ"):format(math.floor(s / 1024)) or r..math.floor(s / 1024).." КБ" s = math.fmod(s, 1024) end if s < 1024 then r = r ~= '' and r..(" %s Б"):format(s) or r..s.." Б" end return r end
Автор: Setuper 15.9.2008, 22:31
Функции преобразования ip адреса в число и обратно:
Код
function Ip2Num(sIP) local a, b, c, d = sIP:match"^(%d+)%.(%d+)%.(%d+)%.(%d+)$" return a * 16777216 + b * 65536 + c * 256 + d end function Num2Ip(i) i = tonumber(i) or 0 local r, d, zd = '', '.', "0." if i >= 16777216 then r = math.floor(i / 16777216)..d i = math.fmod(i, 16777216) else r = zd end if i >= 65536 then r = r..math.floor(i / 65536)..d i = math.fmod(i, 65536) else r = r..zd end if i >= 256 then r = r..math.floor(i / 256)..d i = math.fmod(i, 256) else r = r..zd end return r..i end
Можно использовать для проверки рангов ip и для оптимального хранения ip адресов в бд.
Автор: Setuper 23.9.2008, 17:05
Еще несколько интересных методов. Может кому понадобятся, может кого заинтересует (Я использую их в больших проектах. Они несколько сворачивают длинные выкладки).
Код
tSys={} function tSys:Table(t) return type(t)=="table" and t end function tSys:TableNoVoid(t) return self:Table(t) and next(t) and t end function tSys:TableOrDef(t,d) return self:Table(t) or d or {} end function tSys:TableNoVoidOrDef(t,d) return self:TableNoVoid(t) or self:TableOrDef(t,d) end function tSys:Table2(t,s) return self:Table(t) and self:Table(t[s]) and t[s] end function tSys:TableNoVoid2(t,s) return self:Table2(t,s) and next(t[s]) and t[s] end function tSys:TableOrDef2(t,s,d) return self:Table(t) and (self:Table(t[s]) and t[s] or d or rawset(t,s,{}) and t[s]) or {} end function tSys:TableNoVoidOrDef2(t,d) return self:TableNoVoid2(t) or self:TableOrDef2(t,d) end function tSys:String(s) return type(s)=="string" and s end function tSys:StringNoVoid(s) return self:String(s) and s~="" and s end function tSys:StringOrDef(s,d) return self:String(s) or d or "" end function tSys:StringNoVoidOrDef(s,d) return self:StringNoVoid(s) or self:StringOrDef(s,d) end function tSys:String2(t,s) return self:Table(t) and self:String(t[s]) and t[s] end function tSys:StringNoVoid2(t,s) return self:String2(t,s) and t[s]~="" and t[s] end function tSys:StringOrDef2(t,s,d) return self:Table(t) and (self:String(t[s]) and t[s] or d or rawset(t,s,"") and t[s]) or "" end function tSys:StringNoVoidOrDef2(s,d) return self:StringNoVoid2(s) or self:StringOrDef2(s,d) end function tSys:Number(i) return type(i)=="number" and i end function tSys:NumberOrDef(i,d) return self:Number(i) or d or 0 end function tSys:StringOrNumber(s) return self:String(s) or self:Number(s) end function tSys:NumberOfString(s) return self:String(s) and tonumber(s) and s end function tSys:ToNumber(s) return self:String(s) and tonumber(s) or self:Number(s) end function tSys:NoVoid(s) return self:StringNoVoid(s) or self:Number(s) end function tSys:Eq0(s) return self:Number(s) and s==0 and s end function tSys:Eq1(s) return self:Number(s) and s==1 and s end function tSys:Eq2(s) return self:Number(s) and s==2 and s end function tSys:Eq01(s) return self:Number(s) and (s==0 or s==1) and s end
--метод возвращает количество ВСЕХ полей таблицы (а не только количество индексных полей как функции table.getn или table.maxn) function tSys:GetN(t) local c=0 for _ in pairs(t) do c=c+1 end return c end
P.S. Существуют различия между методами и функциями. tSys:GetN(t) - метод, tSys.GetN(t) - функция
Автор: Sephiroth_Lukaw 3.12.2008, 19:05
Часто их теряю. Так что здесь оставлю. Надеюсь никто не против(знаю, что функция всем известная). /* Serialize(tTable - Сама таблица, sTableName - Имя таблицы, hFile - Хендлер файла, т.е. нужен уже открытый файл) - Сохраняет таблицу в файле. -------------------- SaveTable(sFile - Путь к файлу, tTable - Сама таблица, sTableName - Имя таблицы) - Сохраняет таблицу в файле с помощью функции Serialize - т.е. она должна быть уже определена! -, отличие в том, что она сама открывает файл и закрывает его после выполнения поставленной задачи(т.е. нужен уже не hFile, а путь к нему). */
Код
function Serialize(tTable, sTableName, hFile, sTab) sTab = sTab or '' hFile:write(sTab..sTableName.." = {\n") for k, v in pairs(tTable) do if type(v) ~= "function" then local sKey = type(k) == "string" and ("[%q]"):format(k) or ("[%d]"):format(k) if type(v) == "table" then Serialize(v, sKey, hFile, sTab..'\t') else local sValue = type(v) == "string" and ("%q"):format(v) or tostring(v) hFile:write(sTab..'\t'..sKey.." = "..sValue) end hFile:write(",\n") end end hFile:write(sTab.."}") end
function SaveTable(sFile, tTable, sTableName) local hFile = io.open(sFile, "w+") Serialize(tTable, sTableName, hFile) hFile:close() end
Автор: Setuper 30.1.2009, 13:21
Функция коррекции данных пользователя. Если клиент неизвестен для хаба, то тэг клиента дописывается в описание. Данная функция извлекает тэг из описания и корректно прописывает данные пользователя (такие как слоты, хабы, режим и тд.). Данная функция корректно воспринимает тэг Авалинка.
Код
function GetNormalValue(tUser) if not tUser.sTag and tUser.sDescription then local sDescription,sTag,sClient,sClientVersion,sMode,iNormalHubs,iRegHubs,iOpHubs,iSlots,iLlimit = tUser.sDescription:match"(.*)(<(.+)%sV?:?(.-),M:(.-),H:(%d+)/(%d+)/(%d+),S:(%d+)>)$" if not sDescription then sDescription,sTag,sClient,sClientVersion,sMode,iNormalHubs,iRegHubs,iOpHubs,iSlots,iLlimit = tUser.sDescription:match"(.*)(<(.+)%sV?:?(.-),M:(.-),H:(%d+)/(%d+)/(%d+),S:(%d+),L:(%d+)>)$" end if not sDescription then sDescription,sTag,sClient,sClientVersion,sMode,iNormalHubs,iRegHubs,iOpHubs,iSlots,iLlimit = tUser.sDescription:match"(.*)(<(.+)%sV?:?(.-),M:(.-),H:(%d+)/(%d+)/(%d+),S:(%d+),B:(%d+)>)$" end tUser.sDescription=sDescription tUser.sTag=sTag tUser.sClient=sClient tUser.sClientVersion=sClientVersion tUser.sMode=sMode tUser.iNormalHubs=tonumber(iNormalHubs) tUser.iRegHubs=tonumber(iRegHubs) tUser.iOpHubs=tonumber(iOpHubs) tUser.iHubs=tonumber(iNormalHubs and iRegHubs and iOpHubs and (iNormalHubs + iRegHubs + iOpHubs)) tUser.iSlots=tonumber(iSlots) tUser.iLlimit=tonumber(iLlimit) end return tUser end
Использование метода:
Код
function UserConnected(tUser) tUser=GetNormalValue(tUser) ... end
Автор: alex82 7.2.2009, 17:28
Функция форматирования шары, решающая сию проблему чисто математически.
Код
function GetNormalSize(size, num) size = size or 0 local tSize, sSize = {" B"," kB"," MB"," GB"," TB"," PB"}, "" for i in pairs(tSize) do if size < 1000 then sSize = tSize[i] break end size = size/1024 end local tmp1, tmp2 = 1, 0 if size > 0 then num = num or 3 while size < 10^num do size, tmp1 = size*10, tmp1*10 end size, tmp2 = math.modf(math.floor(size)/10); tmp2 = tmp2*10 if tmp2 >=5 then size = size+1 end size = size/tmp1*10 end return tostring(size)..sSize end
Первый аргумент - собственно размер шары в байтах, второй - до скольки знаков округлить значение шары. Если второй аргумент отсутствует, шара округляется до 3-х знаков.
Автор: Setuper 7.2.2009, 18:25
Недоработанный алгоритм. Простейшая проверка GetNormalSize(1052, 4) не даёт 4 знака после запятой (1.0273), а округляет до 1.027, то есть до третьего знака.
Автор: alex82 7.2.2009, 18:52
Цитата(Setuper @ 7.2.2009, 17:25)
Недоработанный алгоритм. Простейшая проверка GetNormalSize(1052, 4) не даёт 4 знака после запятой (1.0273), а округляет до 1.027, то есть до третьего знака.
А там разве сказано "после запятой"?
Автор: Setuper 7.2.2009, 19:07
А что тогда означает выражение: "до скольки знаков округлить значение шары" ??
Код
function GetNormalSize(s, n) s=s and tonumber(s) or 0 n=n and "%."..n.."f" or "%.3f" if s>=1125899906842624 then return string.format(n,s/1125899906842624).." ПБ" elseif s>=1099511627776 then return string.format(n,s/1099511627776).." TБ" elseif s>=1073741824 then return string.format(n,s/1073741824).." ГБ" elseif s>=1048576 then return string.format(n,s/1048576).." МБ" elseif s>=1024 then return string.format(n,s/1024).." КБ" else return s.." Б" end end
Автор: sphinx 7.2.2009, 19:16
Код
function getNormalShare(share) local i,tUnits = 1, {"B","KB","MB","GB","TB","PB","EB"} while share > 1024 do share = share / 1024 i = i + 1 end return string.format("%.3f",share).." "..(tUnits[i] or "??") end
Автор: alex82 7.2.2009, 19:22
Цитата(Setuper @ 7.2.2009, 18:07)
А что тогда означает выражение: "до скольки знаков округлить значение шары" ??
Это означает что если значение 543.905234 GB, то вернётся 544 GB, а если 1.1834456 GB, то 1.18 GB
Автор: Setuper 7.2.2009, 20:33
Не знаю для чего нужно округлять до количества цифр, обычно округляют до какого-то знака после запятой, ну да ладно.
Самый быстрый по выполнению мой код, так как в ветке выполнения содержится минимальное количество операций, однако очень порадовал код sphinx-а))) :
Код
function GetNormalShare(s,n) local i,t=1,{"Б","КБ","МБ","ГБ","ТБ","ПБ"} s=s and tonumber(s) or 0 n=n and "%."..n.."f " or "%.3f " while s>0x400 do s,i=s/0x400,i+1 end return n:format(s)..(t[i] or "??") end
1) Цикл пробегается от меньшего значения - оптимизация прохода (большинство пользователей не имеют большой шары) 2) Цикл while, а не for, поэтому для выхода из цикла не нужен оператор break (в случае прохода по for i=1,6 нужен был оператор break) 3) Код не громоздкий
Автор: alex82 7.2.2009, 21:05
Цитата
Не знаю для чего нужно округлять до количества цифр, обычно округляют до какого-то знака после запятой
Из соображений целесообразности. Ведь если шара 954.542324234 GB, то имеет смысл избавиться от всех знаков после запятой, поскольку такая точность в большинстве случаев не нужна. Ну а если 1.978678, то нужно оставить хотябы 2 знака потому что 1.978678 и 1 отличаются почти в 2 раза (хотя, юзеров с такой шарой надо банить, а не формарировать их шару )
PS. Какой смысл в этом выражении:
Код
s=s and tonumber(s) or 0
Не проще ли?
Код
s=s or 0
Автор: Setuper 7.2.2009, 21:10
дело в том, что в некоторых случаях шара из себя представляет строку: "47345783456", поэтому строка приводится к числу, хотя это можно выбросить, если контролировать это.
Автор: alex82 9.2.2009, 22:54
Код
function ValidateNickArrival(user,data) Core.SendToUser(user, "Здесь пишем наше сообщение") end
Функция отправляет юзеру сообщение при входе на хаб. При этом сообщение отправляется раньше, чем остальные, в том числе и "копирайт" PtokaX. Выглядит это примерно так:
Автор: Setuper 15.2.2009, 22:40
Аналог стандартной LUA функции для PtokaX:
Код
function _G.print(...) local m = '' for i = 1, _G.select('#', ...) do m = m..tostring(_G.select(i, ...)) end Core.SendToAll(m) end
Функция, преобразующая десятичное число в бинарную строку:
Код
function tobin(n) local m, x = '' while n >= 2 do n, x = _G.math.modf(n / 2) m = 2 * x..m end return n ~= 0 and '1'..m or '0' end
Функция, преобразующая бинарную строку в десятичное число:
Код
tonumber(str, 2)
Можно использовать для раздачи собственных прав профилям хаба. Например, на хабе профили: 0 - Master, 1 - Operator, 2 - VIP, 3 - Reg, -1 - UnReg. Профили можно записать в двоичную строку. Будем полагать, что строка "11111" разрешает всем пяти профилям то или иное действие, строка "10000" разрешает действие только профилю Master. Думаю, что ясен смысл.
Автор: Setuper 16.2.2009, 14:17
Работа со стеком:
Код
top=0; data={}; push=function(name,...) top=top+1 name=name or top data[top]=name data[tostring(name)]=top return name end pop=function(...) local name=table.remove(data) top=top-1 if data[name] then data[name]=nil end return name end getindex=function(name,...) return name and -top+data[tostring(name)]-1 or 0 end clean=function() top,data=0,{} end
Функция push помещает элемент в стек, функция pop извлекает последний элемент из стека, функция getindex возвращает глубину элемента в стеке, функция clean очищает стек.
Автор: Setuper 18.2.2009, 21:17
Очередная интересная по реализации функция форматирования шары:
Код
local t={"Б","КБ","МБ","ГБ","ТБ","ПБ"} local function loop(i,n,c) if i<0x400 then coroutine.yield(n:format(i)..(t[c] or "??"))end return loop(i/0x400,n,c+1) end GetNormalShare=coroutine.wrap(function(s,n)return loop(s and tonumber(s) or 0,n and "%."..n.."f " or "%.3f ",1)end)
Использование:
Код
Core.SendToAll(GetNormalShare(142589996842624,4))
Автор: Setuper 1.4.2009, 17:20
Пример работы с переменным числом параметров:
Код
function MyFunc1(...) local m='' for i=1,select('#',...) do m=m..tostring(select(i,...)) end Core.SendToAll(m) end
Вызов функции:
Код
MyFunc1("qwerty", "5", 1, 0x22b==555)
Результат вызова:
Код
qwerty51true
Если параметры функции состоят только из строк и/или чисел, то функцию можно упростить, записав так:
Код
function MyFunc2(...) Core.SendToAll(table.concat({...})) end
Вызов функции:
Код
MyFunc2("qwerty", "5", 1)
Результат вызова:
Код
qwerty51
Автор: Setuper 3.4.2009, 19:25
Булева алгебра.
Код
x inverse 1 0 0 1
x y and 0 0 0 1 0 0 0 1 0 1 1 1
x y or 0 0 0 1 0 1 0 1 1 1 1 1
x y xor 0 0 0 1 0 1 0 1 1 1 1 0
x y xnor 0 0 1 1 0 0 0 1 0 1 1 1
x xor y = x and inverse y or inverse x and y x xnor y = x and y or inverse(x and y) x xnor y = inverse(x xor y) x xor y = inverse(x xnor y) (x xor y) and (x xnor y) = 0 (x xor y) or (x xnor y) = 1
Функции для работы с бинарным представлением чисел, хотя числа подставляются в десятичном виде
Код
function odd(x) return math.floor(x / 2) * 2 ~= x end
function bit_inverse(x) local c, s = 0, 1 while x > 0 do if not odd(x) then c = c + s end s = s * 2 x = math.floor(x / 2) end return c end
function bit_or(x, y) local c, s = 0, 1 while x > 0 or y > 0 do if odd(x) or odd(y) then c = c + s end s = s * 2 x = math.floor(x / 2) y = math.floor(y / 2) end return c end
function bit_and(x, y) local c, s = 0, 1 while x > 0 or y > 0 do if odd(x) and odd(y) then c = c + s end s = s * 2 x = math.floor(x / 2) y = math.floor(y / 2) end return c end
function bit_xor(x, y) local c, s = 0, 1 while x > 0 or y > 0 do if odd(x) and not odd(y) or odd(y) and not odd(x) then c = c + s end s = s * 2 x = math.floor(x / 2) y = math.floor(y / 2) end return c end
function bit_xnor(x, y) local c, s = 0, 1 while x > 0 or y > 0 do if odd(x) and odd(y) or not odd(y) and not odd(x) then c = c + s end s = s * 2 x = math.floor(x / 2) y = math.floor(y / 2) end return c end
Автор: zangriEBP 22.4.2009, 16:45
Функции для перевода DEC в HEX и обратно
CODE
function to_hex(n) if(type(n) ~= "number") then error("non-number type passed in.") end -- checking not float if(n - math.floor(n) > 0) then error("trying to apply bitwise operation on non-integer!") end if(n < 0) then -- negative n = bit.tobits(bit.bnot(math.abs(n)) + 1) n = bit.tonumb(n) end hex_tbl = {'A','B','C','D','E','F'} hex_str = "" while(n ~= 0) do last = math.mod(n, 16) if(last < 10) then hex_str = tostring(last) .. hex_str else hex_str = hex_tbl[last-10+1] .. hex_str end n = math.floor(n/16) end if(hex_str == "") then hex_str = "0" end return hex_str end function to_dec(hex) if(type(hex) ~= "string") then error("non-string type passed in.") end head = string.sub(hex, 1, 2) if( head ~= "0x" and head ~= "0X") then error("wrong hex format, should lead by 0x or 0X.") end v = tonumber(string.sub(hex, 3), 16) return v; end -- hex lib interface hex = { to_dec = to_dec, to_hex = to_hex, }
Функции для перевода BIN в HEX и обратно
CODE
function hex2bin(hex) local bin_tbl = {[48]='0000',[49]='0001',[50]='0010',[51]='0011',[52]='0100',[53]='0101',[54]='0110',[55]='0111',[56]='1000',[57]='1001',[65]='1010',[66]='1011',[67]='1100',[68]='1101',[69]='1110',[70]='1111'} local r,j, b = '' for i=1,#hex do b=hex:byte(i) if (b>47 and b<58) or (b>64 and b<71) then j=bin_tbl[b] else return -1 end if i==1 then j=tonumber(j) end r=r..j end return r; end
function bin2hex(bin) local hex_tbl = {['0000']='0',['0001']='1',['0010']='2',['0011']='3',['0100']='4',['0101']='5',['0110']='6',['0111']='7',['1000']='8',['1001']='9',['1010']='A',['1011']='B',['1100']='C',['1101']='D',['1110']='E',['1111']='F'} local r,b='' for i=#bin,1,-4 do b=bin:sub(i-3,i) if i<4 then b=string.rep('0',4-i)..bin:sub(1,i):sub(-1*i) end r=r..hex_tbl[b] end return r:reverse() end
Автор: Setuper 22.4.2009, 19:01
Во-первых, видимо math.fmod, а не math.mod. Во-вторых, что за таблица bit? Её и функции tobits, tonumb и bnot нужно определять! В-третьих, к чему все эти проверки и вызовы функции error? Ведь если человек использует подобного рода функции, то он явно понимает, что делает, а эти проверки только делают медленнее алгоритм перевода чисел. В-четвёртых, таблица hex тоже непонятно для чего тут написана. В-пятых, переменные внутри функции надо объявлять локальными
Код
local hex_str = ""
Автор: Setuper 27.4.2009, 12:07
Функция explode разбивает строку указанным разделителем на подстроки. Возвращает таблицу с подстроками.
Код
_G.string.explode = function(self, Sep) local ret = {} for k in (self..Sep):gmatch(("(.-)%s+"):format(Sep)) do _G.table.insert(ret, k) end return ret end
Автор: alex82 27.4.2009, 14:48
Вопрос.
Код
_G.string.explode
Зачем использовать переменную _G, если можно просто string.explode?
Рискну и я три копеечки добавить. Решал проблему разбиения на части длинных посылок (отчетов) Оператору, содержащих более 128000 символов, и вот попутно соорудил такое нехитрое форматирование. Когда нужно упихнуть длинный текст в заданные рамки ( переменная iMargin ). Не шедевр мирового программирования, но, может быть, кому-нибудь пригодится
Код
function CutText(s) local txt,sReturn = s.." ","" local Len,Ln,ln,stemp = txt:len(),0,0,"" for word in string.gmatch(txt, "%S+%s+" ) do local wlen = word:len(); Ln,ln = Ln + wlen,ln+wlen if ln >= iMargin then sReturn = sReturn..stemp.."\r\n\t"; stemp = word; ln = wlen else stemp = stemp..word end if Ln == Len then sReturn = sReturn..stemp; end end return sReturn end
На картинке результат форматирования Описания хаба при разном заданном значении предельной длины подстроки.
Автор: Setuper 26.5.2009, 17:44
Очень не стандартный, но классный метод записи мультистрок:
Код
do local mt = {} function mt:__call(str) table.insert(self, str) return self end function mt:__unm() return table.concat(self, "\n") end function L(...) return setmetatable({...}, mt) end end
local p = -L "line 1" "line 2" "line 3"
Core.SendToAll(p)
Попробуйте разобраться, что происходит в данном примере
Ещё одна достаточно интересная реализация:
Код
function prnt(x) if x then Core.SendToAll(x) return prnt end end prnt "one" "two" "three"
Автор: Wariner 8.6.2009, 16:17
функция форматирования времени
Код
function GetNormalTime(s) s=tonumber(s) or 0 local r="" if s>=31104000 then r=math.floor(s/31104000).." г. " s=math.fmod(s,31104000) end if s>=2592000 then r=r..math.floor(s/2592000).." мес. " s=math.fmod(s,2592000) end if s>=86400 then r=r..math.floor(s/86400).." д. " s=math.fmod(s,86400) end if s>=3600 then r=r..math.floor(s/3600).." ч. " s=math.fmod(s,3600) end if s>=60 then r=r..math.floor(s/60).." мин. " s=math.fmod(s,60) end return r..s.." сек." end
Автор: Setuper 8.2.2010, 19:47
Функция побитового "и":
Код
function bin_and(x, y) if y <= x then local i, r, a, b = 1, 0, 0, 0 while 0 < y do x, a = math.modf(x / 2) y, b = math.modf(y / 2) if 0 < a and 0 < b then r = r + i end i = 2 * i end return r end return bin_and(y, x) end
Автор: Serx 25.2.2010, 22:03
Если у файла в названии есть кириллица, то его имя преобразовывается в какой-то код (в магнет-ссылке) Например "мистер" в %D0%BC%D0%B8%D1%81%D1%82%D0%B5%D1%80 Нагуглил только то, что это URI Кодирование, но откуда берутся "%D0" или "%D1" не понимаю... Ну и сам вопрос: как в LUA произвести эту конвертацию?
Автор: Setuper 26.2.2010, 0:35
Всё достаточно просто. Переводим текст в UTF8 и каждый "нужный" байт представляем в 16-ричном виде
Функцию AnsiToUtf8 берём из первого поста.
Код
function URICoding(sText) local function Check(i) if i < 33 or 126 < i then return nil end if i == 33 then return true elseif 38 < i and i < 43 then return true elseif 44 < i and i < 47 then return true elseif 47 < i and i < 58 then return true elseif 64 < i and i < 91 then return true elseif 96 < i and i < 123 then return true elseif i == 126 then return true end end local s, r = AnsiToUtf8(sText), '' for i = 1, #s do local b = s:byte(i) if Check(b) then r = r..string.char(b) else r = r..("%%%X"):format(b) end end return r end
Автор: Nickolya 26.2.2010, 11:01
В библиотеке LuaSocket есть модуль URL в котором есть функция заменяющая URICoding:
Цитата
url.escape(content)
Applies the URL escaping content coding to a string Each byte is encoded as a percent character followed by the two byte hexadecimal representation of its integer value.
Аналогично можно перевести из полученной строки в обычный текст, надо перекодировать из утф, заменить + на пробел и применить к строке функцию url.unescape
Автор: Ksan 5.5.2010, 5:27
Ещё одна функция приведения к нижнему регистру (ранее нигде не встречал):
Код
function StringLower(arg) os.setlocale"Russian_Russia.1251" -- (русс.локаль) arg = string.lower(arg) os.setlocale("C") -- (возврат на станд.локаль) return arg end
Работает как с кириллицей, так и с латиницей. Аналогично же и для приведения к верхнему регистру:
Код
function StringUpper(arg) os.setlocale"Russian_Russia.1251" -- (русс.локаль) arg = string.upper(arg) os.setlocale("C") -- (возврат на станд.локаль) return arg end
PS: Уж больно подозрительно простенький код получился, но работает. Если у кода есть побочные вредные эффекты, умные товарищи, надеюсь, не пропустят и подскажут.
Автор: Nickolya 5.5.2010, 13:39
А смысл тасовать локали? При старте скрипта обозначил на русскую и пользуйся себе чисто string.lower и string.upper
Автор: Ksan 5.5.2010, 13:40
Если так, то ещё лучше. Это я и хотел услышать от старшего товарища.
Автор: Setuper 5.5.2010, 20:14
Играть с локалями нужно в функциях сохранения в файл, так как в Русской локали разделителем дробной и целой части в числе является запятая, а не точка!
Я это уже объяснял на хабе и повторяю для тех, кто не видел эти разъяснения.
Автор: Nickolya 5.5.2010, 22:13
Т.е. если локаль не русская и мы загружаем файл, который сохранялся из-под русской локали, мы получим ошибку, так? Но если обозначить локаль до загрузки файла в скрипт, будет ли ошибка при загрузке?
Автор: Setuper 5.5.2010, 23:54
Дело в том, что в lua разделителем дробной и целой части является точка (и этот символ не зависит от локали, а является синтаксическим правилом). Поэтому для того чтобы загружать файл при помощи lua функции dofile нужно чтобы внутри файла был исключительно lua формат.
Однако если мы сохраняем файл и при этом установлена русская локаль, то явно или неявно функция tostring преобразует разделитель из точки в запятую и после этого загрузить такой файл функцией dofile уже не удастся.
Поэтому делаем так: везде используем русскую локаль, установленную один раз, но перед тем как сохранить в файл устанавливаем локаль "C", сохраняем в файл, и после сохранения опять возвращаем русскую локаль. Таким образом стандартную локаль нужно устанавливать только на время сохранения информации в файл (для того чтобы оставить в сохранности lua синтаксис в файле).
Автор: Ksan 6.5.2010, 1:03
Как это ни странно, установка локали на русскую именно при старте скрипта не помогает (пробовал включить и в StartUp и до него отдельной строкой). Приходится это делать в ChatArrival'е, перед применением функции string.lover(s).. (либо выносить отдельной функцией, как я выше показывал). Хотя вроде не должно бы иметь значения, переключать в начале скрипта до ЧатАрривала или перед применением.
Автор: alex82 6.5.2010, 12:54
Цитата
Как это ни странно, установка локали на русскую именно при старте скрипта не помогает
Наверное один из нижестоящих скриптов меняет локаль обратно на "C". Дело в том, что локаль меняется глобально - для всего приложения. Поэтому не рекомендуется менять локаль в скриптах для птоки - изменив ее в одном скрипте, ты рискуешь нарушить работу всех остальных.
Автор: alex82 6.5.2010, 16:12
Функции преобразования регистров:
Код
function string.lower2(s) return s:gsub("([А-Я])",function(str) return string.char(str:byte()+32) end):gsub("Ё", "ё"):lower() end
function string.upper2(s) return s:gsub("([а-я])",function(str) return string.char(str:byte()-32) end):gsub("ё", "Ё"):upper() end
Работают быстрее, чем все аналогичные функции, выложенные ранее в этой теме. __________________________________________________
Функция экранирования "волшебных" символов регулярных выражений Lua (собственно, даже не функция, а метод):
Код
:gsub("[%^%$%(%)%%%.%[%]%*%+%-%?]","%%%1")
Метод необходим при использовании произвольных строк в качестве второго аргумента функций string.gsub(), string.match(), string.gmatch(). Пример:
Код
local result = data:gsub(str:gsub("[%^%$%(%)%%%.%[%]%*%+%-%?]","%%%1"),repl)
Стоит напомнить, что в третьем аргументе функции string.gsub() необходимо экранировать символ %, иначе функция будет "съедать" этот символ, или вызывать ошибку "Invalid capture index". Экранирование производится так:
Единственное препятствие - это функции сохранения. Однако это легко устраняется.
Поэтому не вижу никаких препятствий в установки русской локали.
Автор: Ksan 6.5.2010, 23:33
Не вижу проблему в установке локали - поставил локаль, инвертнул регистр, вернул локаль. Всё это делается в 1 микросекунду. Не понимаю, чем это может помешать остальным скриптам? Только разве что религия не позволяет...
Автор: alex82 7.5.2010, 4:26
Цитата(Ksan @ 6.5.2010, 23:33)
Не вижу проблему в установке локали - поставил локаль, инвертнул регистр, вернул локаль.
Только работать такая функция будет раза в 2 медленнее, чем те, что выложил я.
PS. Если вам не нравятся функции, выложенные мной - используйте другие. Я выложил их в надежде, что они окажутся полезными кому-нибудь, а вовсе не для того, чтобы развязать холивар.
Автор: Phazeus 18.6.2010, 2:39
Анализ производительности участка кода:
Код
local test_counter=0 local test_startTime=os.time() while test_startTime==os.time() do end test_startTime=os.time() while os.time()==test_startTime do test_counter=test_counter+1 [КОД_ДЛЯ_АНАЛИЗА] end return tostring(test_counter)
Внутри цикла, вместо "[КОД_ДЛЯ_АНАЛИЗА]" помещаем исследуемый код, который будет выполняться в цикле ровно 1 секунду. Затем возврат переменной test_counter, в которой содержится число итераций цикла за секунду. Чем быстрее выполняется исследуемый участок кода, тем больше это число.
Подсчёт количества строк в тексте (строки разделяются символом новой строки):
Код
function GetLinesCount(str) return #str:gsub("[^\010]","")+1 end
Вернёт число строк в тексте "str", причём если строка пустая, просто 2 раза перевод строки, то будет засчитана как отдельная строка.
Автор: Phazeus 28.6.2010, 2:25
Имейте в виду, что...
Метод
Код
for i in pairs(t) do
работает примерно в 6 раз медленее, чем
Код
for i=1,#t do
Таким образом, для перебора элементов таблицы с последовательными числовыми индексами лучше пользоваться вторым способом.
Ещё интересный факт. Если мы имеем строку, которую нужно преобразовать в число (строка заведомо является корректным числом), то быстрее работает метод преобразования в число проведением какой-либо математической операции, например, сложения. Таким образом, вариант:
Код
local str="12345" str=str+0
работает примерно в 1,8 раза быстрее, чем такой вариант:
Код
local str="12345" str=tonumber(str)
Это касается и метода tostring(n), который, хоть и совсем немного, но медленнее, чем n.."".
Автор: Setuper 28.6.2010, 9:01
При этом функция tostring отработает корректно вне зависимости от содержимого, то есть даже если не определён метаметод __tostring, что нельзя сказать о неявном преобразовании, которое происходит при конкатенации. Поэтому тут палка с двумя концами: либо быстродействие, либо стабильная работа скрипта.
Кстати, сравнивать цикл
Код
for i=1,#t do
и цикл
Код
for i in pairs(t) do
как минимум не корректно, так как второй является более широким. Если уж и сравнивать, то с циклом
Код
for i in ipairs(t) do
Кроме этого также имеет значение то, что мы делаем внутри цикла. Дело в том, что в первом цикле при обращении к элементу таблицы нам нужно будет вызывать операцию получения значения по индексу t[i]. Во втором же варианте значение автоматически возвращается вторым аргументом:
Код
for i,v in ipairs(t) do
i - это индекс, v - это значение. Поэтому всё зависит от ситуации, в которой мы используем цикл
Автор: ivan683 14.7.2010, 20:31
Сделал копию топика с другого форума чтобы можно было здесь продолжить начатое там дело.
Код
function new_string_builder() local string_table = {} local object = {}
function object.insert(...) for idx, str in ipairs({...}) do table.insert(string_table, idx, str) end end
function object.add(...) for idx, str in ipairs({...}) do table.insert(string_table, str) end end
function object.get(spliter) spliter = spliter or "" return table.concat(string_table, spliter) end
function object.empty() return not next(string_table) end
function object.len() local len = 0 for index, str in ipairs(string_table) do len = len + #str end return len end
return object end
Данная функция возвращает "объект" который используется как буфер при генерации очень большого текста составляемого из большого количества маленьких кусочков.
Использование.
Код
buf = new_string_builder()
for i = 0, 10 do buf.add(i.." ") end
print(buf.get())
Результат.
Код
0 1 2 3 4 5 6 7 8 9 10
Автор: Setuper 14.7.2010, 22:44
Данный код годится только для старой версии lua. Для lua 5.1 нужно самому определять arg.
Код
local arg = {...}
Кстати да, для склада lua функций уже есть отдельная тема
Автор: Nickolya 26.7.2010, 0:42
ivan683, http://mydc.ru/index.html?showtopic=753&view=findpost&p=31916, в твоем посту я заменил все в соответствии с замечанием Setuper'а, если есть желание - выкладывай и те функции что были.
Автор: alex82 17.1.2011, 5:44
Оптимизированная функция Serialize:
Код
function Serialize(tTable, sTableName, hFile, sTab) sTab = sTab or "" hFile:write(sTab,sTableName," = {\n") for key, value in pairs(tTable) do local sKey = (type(key) == "string") and ("[%q]"):format(key) or ("[%d]"):format(key) if type(value) == "table" then Serialize(value, sKey, hFile, sTab.."\t") else hFile:write(sTab,"\t",sKey," = ",(type(value) == "string") and ("%q"):format(value) or tostring(value)) end hFile:write(",\n") end hFile:write(sTab,"}") end
Функция, выводящая содержимое таблицы в консоль:
Код
function table.print(tTable, sTableName, sTab, bComma) sTab = sTab or "" print(sTableName and sTab..sTableName.." = {" or sTab.."{") for key, value in pairs(tTable) do local sKey = (type(key) == "string") and ("[%q]"):format(key) or ("[%d]"):format(key) if type(value) == "table" then table.print(value, sKey, sTab.."\t",true) else print(sTab.."\t"..sKey.." = "..((type(value) == "string") and ("%q"):format(value) or tostring(value))..",") end end print(sTab.."}"..(bComma and "," or "")) end
Параметр sTableName указывать не обязательно. Параметры sTab и bComma используются при рекурсивном вызове функции. Следовательно, не нужно указывать их при вызове вручную.
Автор: Setuper 17.1.2011, 10:02
Вместо tprint наверное table.print ? И если функция print стандартная lua функция, то наверное лучше в ней тоже не использовать конкатенацию, а сделать как в Serialize. То есть, вместо
Код
print(a..b..c..d)
юзать
Код
print(a, b, c, d)
а на вставку табов между аргументами я думаю можно забить, хотя возможно будет кривовато отображаться
Автор: alex82 17.1.2011, 17:30
Цитата(Setuper @ 17.1.2011, 9:02)
Вместо tprint наверное table.print ?
Исправил
Цитата(Setuper @ 17.1.2011, 9:02)
И если функция print стандартная lua функция, то наверное лучше в ней тоже не использовать конкатенацию, а сделать как в Serialize. То есть, вместо
Код
print(a..b..c..d)
юзать
Код
print(a, b, c, d)
а на вставку табов между аргументами я думаю можно забить, хотя возможно будет кривовато отображаться
Оно действительно будет некрасиво отображаться. Совсем некрасиво.
Поскольку функция отладочная, на оптимизацию, думаю, можно забить.
Автор: Nickolya 17.1.2011, 21:01
Я как-то в порыве отказа от конкатенаций сделал вот такое, порой использую так же для отладки и раздаю всем для этих же целей, вот код:
Код
function SerializeToString(tTable, sTableName, sTab) local tTableConcat = {} local sTab = sTab or "" table.insert(tTableConcat, sTab) table.insert(tTableConcat, sTableName and sTableName.." = {\r\n" or "return {\r\n") for key, value in pairs(tTable) do local sKey = (type(key) == "string") and ("[%q]"):format(key) or ("[%d]"):format(key) if(type(value) == "table") then table.insert(tTableConcat, SerializeToString(value, sKey, sTab.."\t")) else local sValue = (type(value) == "string") and string.format("%q",value) or tostring(value) table.insert(tTableConcat, sTab) table.insert(tTableConcat, "\t") table.insert(tTableConcat, sKey) table.insert(tTableConcat, " = ") table.insert(tTableConcat, sValue) end table.insert(tTableConcat, ",\r\n") end table.insert(tTableConcat, sTab) table.insert(tTableConcat, "}") return table.concat(tTableConcat) end
Автор: alex82 18.1.2011, 2:07
Функция преобразования кодировки cp1251 в cp866, необходимая для правильного отображения символов кириллицы в консоли Windows:
Код
local CP866 = { [160] = 255, [161] = 246, [162] = 247, [164] = 253, [168] = 240, [170] = 242, [175] = 244, [176] = 248, [178] = 73, [179] = 105, [183] = 250, [184] = 241, [185] = 252, [186] = 243, [191] = 245, } function string.tooem(str) return (str:gsub("[Ђ-я]",function(c) c = c:byte() if c >= 192 and c <= 239 then return string.char(c-64) elseif c >= 240 then return string.char(c-16) elseif CP866[c] then return string.char(CP866[c]) else return "?" end end)) end
Автор: alcorp 2.2.2011, 11:17
Цитата(Serx @ 25.2.2010, 22:03)
Если у файла в названии есть кириллица, то его имя преобразовывается в какой-то код (в магнет-ссылке) Например "мистер" в %D0%BC%D0%B8%D1%81%D1%82%D0%B5%D1%80 Нагуглил только то, что это URI Кодирование, но откуда берутся "%D0" или "%D1" не понимаю... Ну и сам вопрос: как в LUA произвести эту конвертацию?
Имя файла конвертировать НЕ нужно, потому как они по умолчанию в утф8, а вот перекодировать из формата урл, можно следующей функцией:
Код
function unescape (s) s = string.gsub(s, "+", " ") s = string.gsub(s, "%%(%x%x)", function (h) return string.char(tonumber(h, 16)) end) return s end
function CheckPassword(pass) local passlen = #pass if passlen < 2 then return true end
local tpass = {} for i = 1,passlen do table.insert(tpass,pass:sub(i,i)) end
local success = false for i = 2,passlen do if tpass[i] ~= tpass[1] then success = true break end end if not success then return false, "Пароль не может состоять из одинаковых символов" end for i,v in ipairs(tSequence) do success = false local first for n,c in ipairs(v) do if c == tpass[1] then first = n break end end if first then for i = 2,passlen do first = first+1 if not v[first] or v[first] ~= tpass[i] then success = true break end end if not success then return false, "Пароль не может состоять из клавиатурных последовательностей (qwerty, 123456, и.т.д.)" end end end return true end
Функция возвращает false и сообщене об ошибке, если пароль состоит из одинаковых символов или клавиатурной последовательности. В противном случае возвращает true.
Обратите внимание: функция не приводит пароль к нижнему регистру. Вам необходимо сделать это самостоятельно перед вызовом функции.
С table я думаю понятно а вот write_fnc это функция которой по ходу выполнения передаются куски текста. Тем самым давая возможность оптимизировать способ его сборки.
Если результат нужно вывести в файл то делается так:
Ну либо в сочетании со http://mydc.ru/topic334.html?view=findpost&p=31869
Код
var builder = new_string_builder() builder.add("return ") serialize(_G, builder.add) print(builder.get())
Ах да. Забыл сказать о преимуществах. 1 Генерирует красивый и понятный код. 2 Восстанавливает внутренние связи. 3 Проверяет особые случаи (бесконечность, -бесконечность, неопределённость). 4 Дампит функции если возможно.
P.S. совсем забыл что перед результатом деятельности этой функции надо записать либо "x=" либо "return " (Исправил примеры)
Используется мной здесь: http://mydc.ru/r/?https://github.com/ivan386/lua-dht
Автор: Enyby 23.1.2012, 0:46
Функции для создания лога быстрой отладки. Строки лога содержатся в памяти, причем не более чем iMaxFastDebug строк. При добавлении новой строки самый старый вариант удалятся, т. н. ротация сообщений.
Код
local tFastDebug = {} local iMaxFastDebug = 30
function LogFastDebug(sMsg) if #tFastDebug >= iMaxFastDebug then table.remove(tFastDebug, 1) end table.insert(tFastDebug, sMsg) end function GetFastDebug() local sRet = "" for i, sMsg in ipairs(tFastDebug) do sRet = sRet..i..". "..sMsg.."\n" end return sRet end
Пример использования
Сообщает раз в 10 секунд операторам лог входа/выхода пользователей.
Код
function UserConnected(tUser) LogFastDebug(os.time().." "..tUser.sNick.." connected") end OpConnected, RegConnected = UserConnected, UserConnected
function UserDisconnected(tUser) LogFastDebug(os.time().." "..tUser.sNick.." disconnected") end OpDisconnected, RegDisconnected = UserDisconnected, UserDisconnected
function OnStartup() for _, tUser in ipairs(Core.GetOnlineUsers()) do UserConnected(tUser) end TmrMan.AddTimer(10*1000, "CheckPtokaX") end
function CheckPtokaX() Core.SendToOps(GetFastDebug()) end
local tFastDebug = {} local iMaxFastDebug = 30
function LogFastDebug(sMsg) if #tFastDebug >= iMaxFastDebug then table.remove(tFastDebug, 1) end table.insert(tFastDebug, sMsg) end
function GetFastDebug() local sRet = "" for i, sMsg in ipairs(tFastDebug) do sRet = sRet..i..". "..sMsg.."\n" end return sRet end
Автор: Alexey 30.1.2013, 20:56
В Lua5.2 удалили функцию table.maxn и предлагают писать её на Луа самостоятельно, если она действительно нужна. Вот три варианта этой функции, если кому надо:
Код
table.maxn = function(t) local maxn = 0 for i in pairs(t) do if type(i) == "number" and i > maxn then maxn = i end end return maxn end
Код
table.maxn = function(t) local maxn, i = 0 repeat i = next(t,i) if type(i) == "number" and i > maxn then maxn = i end until not i return maxn end
Код
table.maxn = function(t) local maxn, i = 0, next(t) while i do if type(i) == "number" and i > maxn then maxn = i end i = next(t,i) end return maxn end
PS: Понимаю, что в большинстве случаев можно прекрасно обойтись без этой функции, внося минимум правок, но не всегда хочется погружаться в глубины старого чужого говнокода.
Автор: Setuper 31.1.2013, 9:05
Эм... функцию ipairs похоже тоже удалили.
Интересно а оператор #t будет работать?
Автор: Alexey 31.1.2013, 12:11
Функция ipairs на месте, но она тут не подходит, как и оператор #. Стояла цель написать замену table.maxn с максимально похожим поведением, а не что-то другое. Проверка идентичности результатов работы функций на Lua 5.1.4:
Код
local t = {"aaa", "bbb", ["a1"] = "xxx", [5] = "zzz"} print(table.maxn1(t), table.maxn2(t), table.maxn3(t), table.maxn(t), #t)
5 5 5 5 2
Предполагается, что авторы скриптов знали как работает эта функция и применяли её с умом, иначе — ССЗБ и не я им доктор.
19. ipairs, table.maxn, and math.log10 all deprecated
Автор: Alexey 31.1.2013, 21:31
Зачем ссылаться на обсуждение черновой версии, вышедшей за полтора года до релиза? Давай лучше посмотрим в официальное эталонное руководство по Lua 5.2.
Не все изменения из черновика попали в релиз http://mydc.ru/r/?http://www.lua.org/manual/5.2/manual.html#8.2
ipairs осталась в 5.2 http://mydc.ru/r/?http://www.lua.org/manual/5.2/manual.html#pdf-ipairs
Автор: Tsd 22.2.2014, 12:35
Может кому пригодится... Функция автоматически определяет кодировку (ansi\utf-8) в сообщении и возвращает сообщение в ansi, если оно в utf-8 или nil при ошибке конвертации. Для правильной работы необходима библиотека iconv
Код
local tRus = { "А","Б","В","Г","Д","Е",--[["Ё",]]"Ж","З","И","Й","К","Л","М","Н","О","П",--[["Р","С",]]"Т","У","Ф","Х","Ц","Ч","Ш","Щ","Ы","Ъ","Ь","Э","Ю","Я", "а","б","в","г","д","е",--[["ё",]]"ж","з","и","й","к","л","м","н","о","п",--[["р","с",]]"т","у","ф","х","ц","ч","ш","щ","ь","ы","ъ","э","ю","я" }
function DecodeMsg(s) for _,v in pairs (tRus) do if string.find(s,v) then return s end end s = (s:convert("utf-8","cp1251")) return s end
Для функции Utf8ToAnsi(s) из http://mydc.ru/topic334.html?view=findpost&p=2276
Код
function Utf8ToAnsi(s) ... if b < 128 then if nmdc[b] then r = r..nmdc[b] else r = r..string.char(b) end ... end
несовсем очевидна необходимость подобного преобразования протокольных разделителей "$" и "|", ведь вплоть до 128 ASCII-символа ничего не меняется, да и не экранируются они тут – как были в UTF-8 так и остались в ANSI.
Я это вижу так:
Код
function Utf8ToAnsi(s) ... if b < 128 then r = r..string.char(b) ... end
Подскажите, где я не прав?
Автор: Ksan 25.2.2019, 19:51
MIKHAIL, ну проверьте сами. Какие проблемы? (вдаваться в тонкости не стал, сейчас не до того, но добрый совет "проверьте сами" я всегда готов дать)..