Сохранение параметров скрипта QLua(Lua) между запусками

Автор записи: Дмитрий (Admin)
1 звезда2 звезды3 звезды4 звезды5 звезд (Голосов 3, среднее: 5,00 из 5)
Загрузка...

Блоки кода

Функции сохранения и загрузки параметров с примером использования
Данный пример создаст файл ".params" (можно открыть Блокнотом, это обычный текстовый файл, можете назвать его как угодно) со следующим содержимым:

local a = {
   ['a']=10,
   ['d']={
      [1]=100,
      [2]='asdfg',
      [3]=false,
      [4]={
         ['a']=10,
         ['c']=true,
         ['b']='qwerty'
      }
   },
   ['c']=true,
   ['b']='qwerty'
}
return a

Если у Вас появились какие-то вопросы, задайте их в комментариях под статьей !!!

Добавить комментарий

Сохранение параметров скрипта QLua(Lua) между запусками: 66 комментариев

  1. Какова роль в этой ситуации оператора fn = loadstring("return " ..fn)? Может всё дело в логике его работы?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    function read (filename)
       local f,err = io.open(filename,"r")
       if not f then
          return nil,err
       end
       local fn =  "{"..f:read("*a").."}"
       fn = loadstring("return " ..fn)
       f:close()
       if not fn then
          return nil, "Не правильный формат данных в файле\n"..filename
       end
       return fn()
    end
    1. Здравствуйте!
      строка 6: считываем в переменную fn весь файл формата:
      a=10, b={a=10},

      т.к. это строка, то добавляем ей фигурные скобки, но это пока не таблица луа.

      строка 7: создаем функцию fn, которая при выполнении вернет из строки таблицу луа
      строка 12: возвращаем результат выполнения функции fn(), т.е. таблицу:
      {a=10, b={a=10}}

      А какой переменной вы присвоите эту таблицу - ваша забота))

      1. Я уже давно переделал под другие нужды эти функции, и не могу найти, где последний раз их использовал.
        local fn = "{"..f:read("*a").."}" --вот это выражение у меня уже без фигурных скобок

        1. Спасибо большое за уделенное внимание и ценные советы! Буду дальше разбираться. Мощная штука, надо все понять))). Поплыл на регулярных выражениях(((. Сижу, копаю. С Вашего позволения еще потом могу обратиться с вопросом?

            1. Уважаемый Kalikazandr, здравствуйте! Прошу пояснить в части, касающейся регулярных выражений, которые вы применили в своем коде:

              1
              2
              3
              4
              5
              6
              7
              8
              9
              10
              11
              12
              13
              14
              15
              16
              17
              18
              19
              20
              21
              22
              23
              24
              25
              26
              27
              28
              29
              30
              31
              
              function val_to_str (v)
              	if "string" == type( v ) then
              		v = string.gsub( v, "\n", "\\n" ) - меняем \n на \\n: перевод строки в строчной переменной, "экранирующий" обратный слэш?
              		if string.match( string.gsub(v,"[^'\"]",""), '^"+$' ) then - "вычищаем" строчную переменную от апострофов в начале фразы (или что-то ещё)?; ищем совпадение по шаблону '^"+$' - его суть в силу неопытности не понял?
              			return "'" .. v .. "'"
              		end
              		return '"' .. string.gsub(v,'"', '\\"' ) .. '"' - не понял шаблон замены '\\"', зачем ? 
              	end
              	return "table" == type( v ) and  tab_to_str( v ) or tostring( v )
              end
               
              function key_to_str (k)
              	if "string" == type( k ) and string.match( k, "^[_%a][_%a%d]*$" ) then - "^[_%a][_%a%d]*$"? - буквенно-цифровая последовательность от начальной позиции до любой позиции во фразе с заполнением любыми символами до конца? - правильно?
              		return k
              	end
              	return "[" .. val_to_str( k ) .. "]"
              end
               
              function tab_to_str(tbl)
              	local result, done = {}, {}
              	for k, v in ipairs( tbl ) do
              		table.insert( result, val_to_str( v ) )
              		done[ k ] = true
              	end
              	for k, v in pairs( tbl ) do
              		if not done[ k ] then
              			table.insert( result, key_to_str ( k ) .. "=" .. val_to_str ( v ) )
              			end
              	end
              	return table.concat( result, ",\n" )
              end

              Заранее спасибо за уделенное внимание)!

              1. Фишка в том, что в луа таблице пара: ключ = значение - и ключ и значение могут быть чем угодно, соответственно, что бы правильно сделать сохранение (и, что главное, что бы потом все это дело считать правильно из файла) необходимо, проверить и заменить/дополнительно экранировать с начала переменной и до ее конца, обрезать лишние пробелы. Например:
                path={}
                path._Orders = 'C:\\lua_modules\\Orders.lua'
                или так
                path["C:\\lua_modules\\Orders.lua"] = {hint = "лог заявок",{sec_code="SBER",price = 100,qty=10,balance=10}}

                Выражение '^"+$' познаково с начала и до конца строки

                1. Не тратьте на это время. Научитесь сохранять, считывать.
                  Когда ваш алгоритм начнет приносить прибыль, то вам не понадобится знание всех тонкостей луа - за не надобностью. Пить пиво на пляже можно и без знания захватов для форматирования строки)) Я в свое время зря потратил на это время, и на самом деле уже и подзабыл - за не надобностью)))

  2. Дмитрий, здравствуйте! Пытаюсь разобрать код, который в качестве оптимизации предложил kalikazandr, 20 мая 2016 года. Очень интересно, но в силу новизны для меня этого языка сложно полностью понять суть предложения. Для начала интересует функция считывания из файла настроек. Никак не могу получить тот самый элемент - NewParams.d[4].b с помощью кода предложенного kalikazandr. Можете провести аналогию между вашим и его вариантами кода? Где может быть тот самый нюанс?)

    1. Добрый день! Выведет 10-ю строку файла:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      
      -- Пытается открыть файл текущего инструмента в режиме "чтения"
      TradesFile = io.open(TradesFilePath,"r")
      -- Встает в начало файла
      TradesFile:seek('set',0)
      -- Перебирает строки файла
      local Count = 0 -- Счетчик строк
      for line in TradesFile:lines() do
         Count = Count + 1      
         if Count == 10  then
            message(line)
            break
         end
      end
  3. Дмитрий, добрый день!
    Правильно ли я понимаю что строки:
    SaveTable = function(Table, FilePath) и
    LoadTable = function(FilePath) - просто описание функций и их можно заменить на обычные (привычные):
    function SaveTable (Table, FilePath) и
    function LoadTable (FilePath)?

      1. Возможно кому-то, наверно, и мне пригодится запись в статье "ПЕРЕМЕННЫЕ, МАССИВЫ И ФУНКЦИИ В QLUA (LUA)" о такой форме записи при определении функции ~
        MyFunc = function(Param1, Param2) ;
        Спасибо!

      1. Данный код сохраняет массив (таблицу lua) в файл, когда Вы вызываете функцию SaveTable и загружает таблицу из файла (если он существует), когда Вы вызываете функцию LoadTable, а какой массив Вы сохраните в файл это от Вас зависит, а не от скрипта.

            1. Стырила нижевыложенный код и вставила в скрипт Баланс Покупок и продаж в функцию Main.Все работает и сохраняется.Я даже технически не понимаю пока всего как и что,буду разбираться.Не посмотрите,насколько корректно получилось?

              1
              2
              3
              4
              5
              6
              7
              8
              9
              10
              11
              12
              13
              14
              15
              16
              17
              18
              19
              20
              21
              22
              23
              24
              25
              26
              27
              28
              29
              30
              31
              32
              33
              34
              35
              36
              37
              38
              39
              40
              41
              42
              43
              44
              45
              46
              47
              48
              49
              50
              51
              52
              53
              54
              55
              56
              57
              
               function main()
                 -- Создает таблицу
                 CreateTable();
               
                 -- Основной цикл
                 while IsRun do
                    sleep(100);
                 end;
               
                 Array5Min = {}
               
                 function val_to_str ( v )
                 if "string" == type( v ) then
                    v = string.gsub( v, "\n", "\\n" )
                    if string.match( string.gsub(v,"[^'\"]",""), '^"+$' ) then
                       return "'" .. v .. "'"
                    end
                    return '"' .. string.gsub(v,'"', '\\"' ) .. '"'
                 end
                 return "table" == type( v ) and  tab_to_str( v ) or tostring( v )
              end
              --
              function key_to_str ( k )
                 if "string" == type( k ) and string.match( k, "^[_%a][_%a%d]*$" ) then
                    return k
                 end
                 return "[" .. val_to_str( k ) .. "]"
              end
              function tab_to_str( tbl )
                 local result, done = {}
                 for k, v in ipairs( tbl ) do
                    table.insert( result, val_to_str( v ) )
                    done[ k ] = true
                 end
                 for k, v in pairs( tbl ) do
                    if not done[ k ] then
                       table.insert( result, key_to_str ( k ) .. "=" .. val_to_str ( v ) )
                    end
                 end
                 return table.concat( result, ",\n" )
              end
               
               
              function read (filename)
                 local f,err = io.open(filename,"r")
                 if not f then
                    return nil,err
                 end
                 local fn =  "{"..f:read("*a").."}"
                 fn = loadstring("return " ..fn)
                 f:close()
                 if not fn then
                    return nil, "Не правильный формат данных в файле\n"..filename
                 end
                 return fn()
              end
              end
              1. Что-то мне кажется Вы усложнили себе задачу. Вставьте просто функции SaveTable и LoadTable в конец своего скрипта, создайте какой-нибудь массив, в котором Вы будете хранить параметры, которые нужно сохранять между перезапусками. Добавьте в скрипт функцию OnStop, в ней вызывайте SaveTable, передавая туда Ваш массив на сохранение. И добавьте функцию OnInit, в ней вызывайте функцию LoadTable, которая возвратит Вам Ваш сохраненный массив. Вот и все.

  4. Привет.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    function OnInit() -- Функция вызывается терминалом QUIK перед вызовом функции main()
       local f = io.open(getScriptPath().."//Spread_N_62_74.txt","r+") -- Пытается открыть файл состояния в режиме "чтения/записи"
       if f ~= nil then -- Если файл существует, перебирает строки файла, считывает содержимое в соответствующие переменные
          local Count = 0 -- Счетчик строк
          for line in f:lines() do
             Count = Count + 1
             if     Count == 1  then SC_Open   = tonumber(line)
             elseif Count == 2  then SC_Num    = tonumber(line)
             elseif Count == 3  then SC_Volume = tonumber(line)
             elseif Count == 4  then SC_Price  = tonumber(line)
             elseif Count == 5  then SC_Close  = tonumber(line)
    .................. и т.д.

    Вопрос можно ли переменные SC_ записать в одну строку что то типа

    1
    
     if     Count == 1  then SC_Open   = tonumber(line) SC_Num    = tonumber(line) и т.д.

    и потом прочитать их?

      1. 1
        2
        3
        4
        
        function SaveCurrentState() -- Функция сохраняет текущее состояние в файл
           local f = io.open(getScriptPath().."//Spread_L.txt","w") -- Создает файл в режиме "записи"
           -- Записывает в файл текущее состояние
           f:write(SC_Open.."  "..SC_Num.."  "..SC_Volume.."  "..SC_Price.."  "..SC_Close.."  ")

        не знаю как записать

        1. Можешь и так записать, только в конце добавь символ конца строки

          1
          
          f:write(SC_Open..";"..SC_Num..";"..SC_Volume..";"..SC_Price..";"..SC_Close.."\n")

          А потом читай, примерно вот так

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          25
          26
          
          -- Читает из открытого файла существующие сделки в массив
          function ReadTradesHistoryFile()
             -- Встает в начало файла
             TradesFile:seek('set',0);
             -- Перебирает строки файла, считывает содержимое в массив сделок
             local Count = 0; -- Счетчик строк
             for line in TradesFile:lines() do
                Count = Count + 1;
                if Count > 1 and line ~= "" then
                   NewTrade = true;
                   Trades[Count-1] = {};
                   local i = 0; -- счетчик элементов строки
                   for str in line:gmatch("[^;^\n]+") do
                      i = i + 1;
                      if i == 3 then Trades[Count-1].Num = tonumber(str);
                      elseif i == 4 then Trades[Count-1].Date = tonumber(str);
                      elseif i == 5 then Trades[Count-1].Time = tonumber(str);
                      elseif i == 6 then Trades[Count-1].Operation = str;
                      elseif i == 7 then Trades[Count-1].Qty = tonumber(str);
                      elseif i == 8 then Trades[Count-1].Price = tonumber(str);
                      elseif i == 9 then Trades[Count-1].Hint = str; Trades[Count-1].OpenCount = Trades[Count-1].Qty;
                      end; 
                   end;
                end;
             end; 
          end;
                  1. 1
                    2
                    3
                    4
                    5
                    6
                    7
                    8
                    9
                    10
                    11
                    12
                    13
                    14
                    15
                    16
                    17
                    
                     function OnInit() -- Функция вызывается терминалом QUIK перед вызовом функции main()
                       local f = io.open(getScriptPath().."//Spread_N_62_74.txt","r+") -- Пытается открыть файл состояния в режиме "чтения/записи"
                       if f ~= nil then -- Если файл существует, перебирает строки файла, считывает содержимое в соответствующие переменные
                          local Count = 0 -- Счетчик строк
                          for line in f:lines() do
                             Count = Count + 1
                             if Count == 1 then
                                local i = 0; -- Счетчик элементов строки
                                for str in line:gmatch("[^;^\n]+") do
                                   i = i + 1;
                                   if     i == 1 then SC_Open = tonumber(str);
                                   elseif i == 2 then SC_Num = tonumber(str);
                                   elseif i == 3 then SC_Volume = tonumber(str);
                                   elseif i == 4 then SC_Price = tonumber(str);
                                   elseif i == 5 then SC_Close = tonumber(str);
                                   end
                                end
                    1. 1
                      
                        f:write(SC_Open..";"..SC_Num..";"..SC_Volume..";"..SC_Price..";"..SC_Close.."\n")

                      запись так

                    2. вставь между 5 и 6 строками
                      message(tostring(line))
                      а между 9 и 10 строками
                      message(tostring(str))
                      посмотри что считывается

                    3. Разобрался ошибка как всегда идиотская:
                      было

                      1
                      
                        local f = io.open(getScriptPath().."//Spread_N_62_74.txt","r+")

                      надо

                      1
                      
                        local f = io.open(getScriptPath().."//Spread_L.txt","r+")

                      а код правильно работает, спасибо

          1. Здравствуйте, Дмитрий!
            Попробовал данный код для хранения настроек в csv файле.
            Все работает, кроме одного:
            Если в ячейке пусто, то этот элемент между точками с запятой (пустая строка) просто игнорируется.((
            И пятый элемент становится четвертым и так далее.
            Это мне понадобилось при ручной правке csv файла в Экселе.

            1
            2
            3
            4
            5
            6
            7
            8
            9
            
             line='100;200;aaa;bbb;ccc' -- В этом случае все нормально.
            --line='100;200;aaa;;mmm'  -- В этом случае прозевывает четвертый элемент - пустую строку.
             
            vyh=''
            for str in line:gmatch("[^;^\n]+") do
            	vyh=vyh.."/"..tostring(str)
            end
             
            message(vyh)
              1. Нет, дело не в этом, вы не поняли, Дмитрий.

                Скрипт Lua сохраняет файл в csv с разделителями точка с запятой.

                Эксель нормально открывает этот файл.
                Если пользователь сотрет какие-то значения в ячейках, то Эксель сохранит их тоже нормально

                рассмотрим файл в одну строку и 4 ячейки:
                ААА;BBB;;DDD

                Так вот данный код проигнорирует 4 элемент и DDD станет третьим при разборе строки.

                    1. Пардон, искал по кусочку кода в Яндексе.

                      Я помню, что этот чудесный способ хранения таблиц в csv файле я позаимствовал у вас.

                      Просто я часто редактирую вечером эти файлы csv в Экселе, вот и столкнулся с данной неприятностью.

            1. Пока выкручиваюсь, добавляя после каждой точки с запятой собаку и убирая ее на этапе разбора строки на комплектующие.))

              --line='100;200;aaa;bbb;ccc' -- В этом случае все нормально.
              line='100;200;aaa;;mmm' -- В этом случае прозевывает пустую строку.

              line=string.gsub(line,";",";@") -- Добавил справа от кадо1 точки с запятой собаку.

              vyh=''
              for str in line:gmatch("[^;^\n]+") do
              str=string.gsub(str,'@','') -- Убрал все собаки из отделенного элемента.

              vyh=vyh.."/"..tostring(str)
              end

              message(vyh)

              1. Время вопроса (коментария) вижу, но может кому пригодится вариант попроще

                1
                2
                3
                4
                5
                6
                
                	s = '1;2;3;;4'
                	i = 1
                	for x,y in string.gmatch(s..';', "([^;]*);") do
                		message(i .." - '".. tostring(x) .."'")
                		i=i+1
                    end
  5. Добрый день, Дмитрий.
    Очень громоздкий и медленный подход вы избрали для сохранения таблицы в файл.
    a=10 и ['a']=10 одно и тоже.
    Не проще вот такие функции использовать?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    
    function val_to_str ( v )
       if "string" == type( v ) then
          v = string.gsub( v, "\n", "\\n" )
          if string.match( string.gsub(v,"[^'\"]",""), '^"+$' ) then
             return "'" .. v .. "'"
          end
          return '"' .. string.gsub(v,'"', '\\"' ) .. '"'
       end
       return "table" == type( v ) and  tab_to_str( v ) or tostring( v )
    end
    --
    function key_to_str ( k )
       if "string" == type( k ) and string.match( k, "^[_%a][_%a%d]*$" ) then
          return k
       end
       return "[" .. val_to_str( k ) .. "]"
    end
    function tab_to_str( tbl )
       local result, done = {}, {}
       for k, v in ipairs( tbl ) do
          table.insert( result, val_to_str( v ) )
          done[ k ] = true
       end
       for k, v in pairs( tbl ) do
          if not done[ k ] then
             table.insert( result, key_to_str ( k ) .. "=" .. val_to_str ( v ) )
          end
       end
       return table.concat( result, ",\n" )
    end

    и чтение:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    function read(filename)
       local f,err = io.open(filename,"r")
       if not f then
          return nil,err
       end
       local fn =  "{"..f:read("*a").."}"
       fn = loadstring("return " ..fn)
       f:close()
       if not fn then
          return nil, "Не правильный формат данных в файле\n"..filename
       end
       return fn()
    end
      1. Дело не в 10 или 20 строках или читабельности, а в практичности lua.
        Если грамотно использовать эти функции, то читабельность в файле будет лучше, чем у вас, а вот по скорости сохранения раз в 30 быстрее (поначалу лепил такой же примерно код как у вас), объем получаемого файла меньше на лишние скобки, "return a" и прочее не нужное барахло : )
        Согласитесь, если файл настроек велик или файл будет содержать хеш таблицу с данными за последние 30-40 торговых дней, то прирост в скорости будет заметен визуально. Для небольших файлов безусловно пойдет и ваш код.

        1. При навешивании assert на loadstring начинает сообщать о недопустимых символах у параметра local в сформированной строке?! Правда, считываемый файл формируется кодом Дмитрия, а не вашим. Может дело в этом?

        2. Приблизительно понял, все в моем понимании сошлось на ином формате записей файла, ваш код сохранения таблицы работает несколько иначе. Теперь буду думать, как работает часть кода, сохраняющая данные. Спасибо за подкинутую мудрую идею.

    1. Уважаемый Kalikazandr, здравствуйте! Пытаюсь разобрать код, который вы в качестве оптимизации предложили 20 мая 2016 года по предмету промежуточного сохранения данных в период выключения. Мне это очень интересно, но в силу новизны для меня этого языка сложно полностью понять суть предложения. Для начала интересует функция считывания из файла настроек. Никак не могу получить тот самый элемент - NewParams.d[4].b с помощью вашего кода. Скрипт работает, а вот ожидаемого сообщения с информацией о содержимом указанного элемента нет. Можете поподробнее описать суть этого кода, провести аналогию, соответствие между вашим вариантом кода и вариантом Дмитрия? Какова роль в этой ситуации оператора fn = loadstring("return " ..fn)? Может всё дело в логике его работы?