Read and write the same file

Hi guys !
I am trying to write to a document file by using the method system.documentsDirectory.
I realize that I can’t write after reading and also can’t read after writing. only one action at time.

Anyone can Help me please ?

here my codes !!!


local function btnOnPressHandler(event)                                                 ----event button-------------ok
local path = system.pathForFile( "db.txt", system.DocumentsDirectory ) ----open the file path------ok 
   local userid = frmUsernam.text      ------------------------------------------------------input text--------------ok
   
   if(userid == '') then labelReturnStatus1.text = "invalid entry !" ---------------------------------------------ok
   

elseif error then
 local file = io.open( path, "r" ) ----------------------------------------------------Open the file handle---------ok
 for line in file:lines() do           
 if(line == userid ) then  labelReturnStatus1.text = "already existe ! "---------------------------------------ok



elseif error then
 
         local file = io.open( path, "w" )
         file:write(userid) ----------------------------------------------------------------------------------------not works  !!
         --io.close( file )
         --file = nil 

    
       labelReturnStatus1.text = userid---------------------------------------------------------------------------------ok
       labelReturnStatus2.text = "has been added"------------------------------------------------------------------ok
   
   
     end 
    end
   end
  end



I suggest using a pre-written library instead of re-inventing the wheel.

You can get my io.* extensions here:

  1. download the file.
  2. require it: require "io"
  3. Use it: Extensions - Super Starter Kit 2

Ex:

local userName = io.readFile( "db.txt" )
... later
local userName2 == "Bob"
io.writeFile( "db.txt", userName )

Tip:

  1. You’d be best served to get all of my extensions: SSK2/ssk2/core/extensions at master · roaminggamer/SSK2 · GitHub and require them into your project.
  2. Then I’d use tables to store data.
  3. You can use my table.* extensions to save tables to files and read them back:
    Extensions - Super Starter Kit 2

Ex:

local user  = {}
user.name = "Bob"
user.id = 100
user.age = 25

table.save( user, "userTable.txt" )

Reading a saved table works like this:

local user = table.load( "userTable.txt" )
if( user.name ) then
   print( user.name, user.id, user.age)
end

thanks for your reply.
you suggest me a good idea. But my problem is : " I want the user choose a list of players, create, save and delete them as he want". With pre-defined table we have a limited action.

You can use a table to achieve the goals you have. I’m only suggesting a table because it is more flexible and easier to deal with than coming up with a flat text format.

Don’t forget tables can contain tables so if needed you can have a table of users. Also you don’t need to start with a initial table. You can create it as needed.

Anyways, I think the provided io.* and table.* extension should do the job (or you can dig into the code and steal the parts you need).

Last, if you continue and get stuck again, please post back and give a basic sequence of how you intend for this feature to work and to be used. I’ll watch for it and try to give a more comprehensive reply/example.

you describe exactly what I want to do. My job is not a part of the game but just a database that user can create to allow a particular players play a certain scene according to his level. Now I realize that I don’t know anything about databases. Please could you send me a simple complete model that I can see how to do

Here is a pure-code (no GUI) example of a basic user DB with the beginnings of a module to do the work.

https://github.com/roaminggamer/RG_FreeStuff/raw/master/AskEd/2022/05/fileIO_userDB_example.zip

1 Like

Thanks very much it’s very useful.
your example need me to reconsider my project . it need me to re-thank the basic structure. I don’t know if it will be possible I’ll let you know.

let me explain you my real need. according to your scripts the names are pre-edited, and me, I want to create them.

I just want to create a list of name. I need to read the file and check the existent name. if the entry name is exist in the list then the system return me this name is already created. but if this name is not in the list the system will create it.

  1. Yeah, I made the name list in advance because I didn’t want to write an example with GUI elements. I wanted to focus on the file save/load, the ‘db’, and the module to support it all. However I needed some unique names to show the whole thing in action and while I could have generated random strings I though actual names would be nicer. :slight_smile:

  2. You can modify the example module and add a find-by-name function similar to the ‘find-by-id’ one I provided. Then you can let users type names into text fields and check for the existence of that name in your db.

function m.getRecordByUserName( name )
   for i = 1, #m._userDB do
      if( name == m._userDB[i].name ) then 
         return m._userDB[i]
      end
   end
   return nil
end

The above function returns ‘nil’ if name is not found, or the record if it is. Alternately if you want a ‘exists’ function you could do this:

function m.nameExists( name )
   for i = 1, #m._userDB do
      if( name == m._userDB[i].name ) then 
         return true
      end
   end
   return false
end

that is my problem !
according to you , I have to create both functions, one for record and the other one for verification.
how to call both function at time ?

I don’t understand the question. Can your list the sequence of events you’re trying to achieve, including user input?

If I were attempting to allow users to create new ‘accounts’ and I wanted to ensure that users had a unique user-name I would do this:

  1. Create a GUI with a text field for ‘username’ and any other fields needed.

  2. Also add at least two buttons to the GUI: “OK”, and “Cancel”.

  3. Initially disable “OK”.

  4. As soon as the user started typing in a user name I would start checking it against my rules:
    a. Length (probably want a min length of around 8)
    b. Unique (just pass the current value of the field to m.nameExists()
    c. Ensure any other fields that should be filled in are also filled.

  5. If the Other fields filled + Username long enough + Username unique rules are met, enable OK button, otherwise disable it.

  6. When the user clicks OK, add the user data to DB and switch to next interface in app/game.

Write back if:
A. This is not what you want to do. (Please give detailed description)

B. You can’t work out how to do this. (Question: Are you familiar with Corona’s scene manager composer?)

local defaultField
 
local function textListener( event )
 
    if ( event.phase == "began" ) then
        -- User begins editing "defaultField"
 
    elseif ( event.phase == "ended" or event.phase == "submitted" ) then
        -- Output resulting text from "defaultField"
        print( event.target.text )
 
    elseif ( event.phase == "editing" ) then
        print( event.newCharacters )
        print( event.oldText )
        print( event.startPosition )
        print( event.text )
    end
end
 
-- Create text field
defaultField = native.newTextField( 150, 150, 180, 30 )
defaultField:addEventListener( "userInput", textListener )

it’s not like that.


-- INCLUDE REQUIRED LIBRARIES 
local widget = require("widget") -- for status label
local userid = nil
local password = nil
  -- SET BACKGROUND
local background = display.newRect( sceneGroup, 420, 450,500, 500)
background:setFillColor(100,100,100);
-- CREATE LOGIN SCREEN 
-- CREATE LABELS
local labelHeadline = display.newText( sceneGroup, " Add User setting", 0, 0, font, 57)
 labelHeadline.x = 375
labelHeadline.y = 60
--loginScreen:insert(labelHeadline)
local labelUsername = display.newText( sceneGroup, " enter your username", 0, 0, font, 36)
 labelUsername:setTextColor(0, 0, 0)
labelUsername.x = 410 
labelUsername.y = 229
--loginScreen:insert(labelUsername)
local labelPassword = display.newText( sceneGroup, " enter your password", 0, 0, font, 35)
 labelPassword:setTextColor(0, 0, 0)
labelPassword.x = 375
labelPassword.y = 470
--loginScreen:insert(labelPassword)
local labelReturnStatus = display.newText( sceneGroup , "", 0, 0, font, 47)
 labelReturnStatus.x = 400
labelReturnStatus.y = 740
labelReturnStatus:setFillColor( 0, 0, 0 )

local labelReturnStatus1 = display.newText( sceneGroup , "", 0, 0, font, 35)
 labelReturnStatus1.x = 400
labelReturnStatus1.y = 1000
labelReturnStatus1:setFillColor( 0, 0, 0 )

local labelReturnStatus2 = display.newText( sceneGroup , "", 0, 0, font, 35)
 labelReturnStatus2.x = 400
labelReturnStatus2.y = 1040
labelReturnStatus2:setFillColor( 0, 0, 0 )
--loginScreen:insert(labelReturnStatus)
-- CREATE USERNAME TEXT FIELD - ADD TO LOGIN FORM
local frmUsernam = native.newTextField( 0, 0, 360, 100)
    frmUsernam.inputType = "default"
    frmUsernam.font = native.newFont(font, 50)
     frmUsernam.isEditable = true
    frmUsernam.align = "center"
     frmUsernam.x = 415
    frmUsernam.y = 335
    frmUsernam.text = 'Response 1'
-- add login form field to login screen
sceneGroup:insert(frmUsernam)
-- handle field events
function frmUsernam:userInput(event)
    if(event.phase == "began") then
        -- you could implement tweening of object to get out of the way of the keyboard here
      print("Began frmUsername" .. ' ' .. event.target.text)
        event.target.text = ''
   elseif(event.phase == "editing") then
        -- fired with each new character
        print("Editing frmUsername" .. ' ' .. event.target.text)
    elseif(event.phase == "ended") then
        -- fired when the field looses focus as a result of some other object being interacted with
        print("Ended frmUsername" .. ' ' .. event.target.text)
    elseif(event.phase == "submitted") then
        -- you could implement tweening of objects to their original postion here
        print("Submitted frmUsername" .. ' ' .. event.target.text)        
   end
end
frmUsernam:addEventListener("userInput",frmUsernam)
-- CREATE PASSWORD TEXT FIELD - ADD TO LOGIN FORM
local frmPassword = native.newTextField(0, 0, 360, 100)
    frmPassword.inputType = "default"
    frmPassword.font = native.newFont(font, 50)
     frmPassword.isEditable = true
    frmPassword.isSecure = false
    frmPassword.align = "center"
     frmPassword.x = 415
    frmPassword.y = 565
    frmPassword.text = 'Response 2'
-- add login form field to login screen
sceneGroup:insert(frmPassword)
-- handle field events
function frmPassword:userInput(event)
    if(event.phase == "began") then
        -- you could implement tweening of object to get out of the way of the keyboard here
        print("Began Password" .. ' ' .. event.target.text)
        event.target.text = ''
    elseif(event.phase == "editing") then
        -- fired with each new character
        print("Editing Password" .. ' ' .. event.target.text)
    elseif(event.phase == "ended") then
        -- fired when the field looses focus as a result of some other object being interacted with
        print("Ended Password" .. ' ' .. event.target.text)
    elseif(event.phase == "submitted") then
        -- you could implement tweening of objects to their original postion here
        print("Submitted Password" .. ' ' .. event.target.text)        
    end
end
frmPassword:addEventListener("userInput",frmPassword) 
-- MAKE KEYBOARD GO AWAY ON BACKGROUND TAP
function background:tap(event)
    native.setKeyboardFocus(nil)
end
background:addEventListener("tap",background)
-- HANDLE BUTTON PRESS



---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------
local function btnOnPressHandler(event)
local path = system.pathForFile( "db.txt", system.DocumentsDirectory ) ----open the file path------ok 
   local userid = frmUsernam.text      ------------------------------------------------------input text--------------ok
   
   if(userid == '') then labelReturnStatus1.text = "invalid entry !" ---------------------------------------------ok
   

elseif error then
 local file = io.open( path, "r" ) ----------------------------------------------------Open the file handle---------ok
 for line in file:lines() do           
 if(line == userid ) then  labelReturnStatus1.text = "already existe ! "---------------------------------------ok
elseif error then
 
         local file = io.open( path, "w" )
         file:write(userid) ----------------------------------------------------------------------------------------not works  !!
         --io.close( file )
         --file = nil 

    
       labelReturnStatus1.text = userid---------------------------------------------------------------------------------ok
       labelReturnStatus2.text = "has been added"------------------------------------------------------------------ok
   
   
     end 
    end
   end
  end
--------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------





-- create button
local btn = widget.newButton({
 id = "Login Button",
 left = 310, 
 top = 770,  
 label ="Sign up",
 defaultFile="image/button.png",
 overFile ="image/boulble.png",
 width = 200, 
 height = 100, 
 font = font, 
 fontSize = 53,
 labelColor = {  default = {0,0,1},
 over = {255,255,255} },
 defaultColor = {201,107,61}, 
 overColor = {219,146,85},
 onPress = btnOnPressHandler,
 onDrag = btnOnDragHandler,
 onRelease = btnOnReleaseHandler 
  })

-- add button to login screen
sceneGroup:insert(btn)
-- END OF LOGIN SCREEN








Based on your code, this is the best I can come up with on short notice:

https://github.com/roaminggamer/RG_FreeStuff/raw/master/AskEd/2022/05/fileIO_userDB_example2.zip

1 Like

You’re the best !

Thank you very much. Your help is very useful I appreciate it. You give me exactly what I am looking for.
As I said to you, your extension restructure my project. Because I am new ! I will ask you something as per as I modify some scenes.

Hi !
how are-you ?
Now I am working with your codes.
I ignore how to fix the Front case. I see Upper is different than lower. example (USER is not "user").
I also want to limit the font ( numeric only or alphabetical only ) when the user type into text fields. I am trying to do it with no success.

local userid = frmUsernam.text
   local userid_ok = false

   -- Rule A1 - Length >= 4
   if( string.len(userid) < 4 ) then       
      labelReturnStatus1.text = "invalid username (too short)!" 

   -- Rule A2 - Does not already exist
   elseif( user_db_module.nameExists( userid ) ) then       
      labelReturnStatus1.text = "invalid username (already exists)!" 

   -- Clear warning label
   else
      labelReturnStatus1.text = ""
      labelReturnStatus2.text = ""
      userid_ok = true
   end

   -- Abort if user name not OK
   if( not userid_ok ) then return end

Are you saying you want to add these rules for username and password?

  1. Case-insensitive. e.g. ‘user’ treated as ‘same as’ 'USER"
    – I suggest using string.lower() during compares and saves.

  2. Only allow alpha-numeric text (only letters and numbers, no special symbols)
    – I suggest cleaning the name every time the user types a character and replacing the string with the ‘cleaned’ string. There is no textfield/keyboard ‘mode’ to enforce an alpha-numeric ONLY keyboard.

I added this code to the scripts/extensions/string.lua script:

-- See ascii chart here: https://en.wikipedia.org/wiki/ASCII
string.alphaNumericOnly = function( str )
   local s = ""
   for i = 1, str:len() do
      if (str:byte(i) >= 48 and str:byte(i) <= 57) or 
         (str:byte(i) >= 65 and str:byte(i) <= 90)  or 
         (str:byte(i) >= 97 and str:byte(i) <= 122) then
         s = s .. str:sub(i,i)
      end
   end
   return s
end

Then, I modified example.lua

Get this update: https://github.com/roaminggamer/RG_FreeStuff/raw/master/AskEd/2022/05/fileIO_userDB_example3_case_alphanumeric.zip

Then, search for these function calls (in example.lua):

  • alphaNumericOnly
  • string.lower

Thanks !
this code works:
local userid = string.upper(frmUsernam.text)

but I can’t call field 'alphaNumericOnly'.

I would like to know how to block the box on numeric. If the user types alphabetic text it will return an error message saying “numeric only”.
As I told you before, you have already given me what I seek. But as it’s new for me I want to learn more for the future.
You can notice it, I’m not the only one interested, you have more than 7 downloads.

Another question. I will create a scene to visualize this function. is it possible ?

function m.printUsers()
   for i = 1, #m._userDB do
      print( "User #" .. i )
      print( " > Name: " .. m._userDB[i].name )
      print( " > Password: "   .. m._userDB[i].password )
   end
end

Glad to hear it’s working.

alphaNumericOnly is implemented in the newest example in scripts/extensions/string.lua

You can’t really block inputs, but you can detect them and ‘clean’ the text.
alphaNumericOnly cleans text and only leaves behind numbers and letters.

You can create a new function using similar checks as alphaNumericOnly but return true or false indicating whether the forbidden character(s) were found. This boolean returning function can be used in your rule logic to print a message. Then, write a similar function to clean the string and replace it. The order of events would be (in listener):

  1. Detect user edited string
  2. Call your new ‘checking’ function to see if the new text contains an illegal character. If so, print your error message.
  3. Use your other new function to ‘clean’ the text and replace it. Just like I did in the example with alphaNumericOnly

Can you create a scene and print the users to the screen? Yes. I’d put the text in a scrollview.

here is what I do.
in the module “example” I call the “string module” by using this :

if( string.alphaNumericOnly(userid) ) then       
      labelReturnStatus1.text = "sorry text only !"

when I leave the string like you send it, it return the text label for every input type.
if I modify the value I get no "labelReturnStatus.text" but the box bloc the numerical.
my question is
what is the value to bloc only alphabetical text input ?
and
what is the value to bloc only numerical text input ?
I hope that you understand

here is the string module code:

   local s = ""
   for i = 1, str:len() do
      if (str:byte(i) >= 48 and str:byte(i) <= 57) or (str:byte(i) >= 65 and str:byte(i) <= 90)  or (str:byte(i) >= 97 and str:byte(i) <= 122) then
         s = s .. str:sub(i,i)
      end
   end
   return s
end

Add this to your copy of string.lua:

string.isAlphaNumericOnly = function( str )
   for i = 1, str:len() do
      if (str:byte(i) >= 48 and str:byte(i) <= 57) or 
             (str:byte(i) >= 65 and str:byte(i) <= 90)  or 
             (str:byte(i) >= 97 and str:byte(i) <= 122)  then
         -- do nothing, keep parsing
      else
          return false
      end
   end
   return true
end

Now use it like this:

if( string.isAlphaNumericOnly(userid) == false ) then       
      labelReturnStatus1.text = "sorry text only !"

The third example only allows alpha-numeric input. i.e. It blocks special characters.
https://github.com/roaminggamer/RG_FreeStuff/raw/master/AskEd/2022/05/fileIO_userDB_example3_case_alphanumeric.zip