This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
exploder's profile picture

How to restrict the length of a word

Started by exploder, 30 December 2012 - 03:12 AM
exploder #1
Posted 30 December 2012 - 04:12 AM
Hello.

I'm making a login screen where user needs to input username and password and I can't figure out, how to restrict the length that user is allowed to type?
grabie2 #2
Posted 30 December 2012 - 04:24 AM
you have to write custom read function that will not allow you to write if string length is more than specified value

here you have original read function

function read( _sReplaceChar, _tHistory )
term.setCursorBlink( true )
    local sLine = ""
local nHistoryPos = nil
local nPos = 0
    if _sReplaceChar then
  _sReplaceChar = string.sub( _sReplaceChar, 1, 1 )
end

local w, h = term.getSize()
local sx, sy = term.getCursorPos()

local function redraw( _sCustomReplaceChar )
  local nScroll = 0
  if sx + nPos >= w then
   nScroll = (sx + nPos) - w
  end
  
  term.setCursorPos( sx, sy )
  local sReplace = _sCustomReplaceChar or _sReplaceChar
  if sReplace then
   term.write( string.rep(sReplace, string.len(sLine) - nScroll) )
  else
   term.write( string.sub( sLine, nScroll + 1 ) )
  end
  term.setCursorPos( sx + nPos - nScroll, sy )
end

while true do
  local sEvent, param = os.pullEvent()
  if sEvent == "char" then
   sLine = string.sub( sLine, 1, nPos ) .. param .. string.sub( sLine, nPos + 1 )
   nPos = nPos + 1
   redraw()
  
  elseif sEvent == "key" then
	  if param == keys.enter then
    -- Enter
    break
   
   elseif param == keys.left then
    -- Left
    if nPos > 0 then
	 nPos = nPos - 1
	 redraw()
    end
   
   elseif param == keys.right then
    -- Right   
    if nPos < string.len(sLine) then
	 nPos = nPos + 1
	 redraw()
    end
  
   elseif param == keys.up or param == keys.down then
			    -- Up or down
    if _tHistory then
	 redraw(" ");
	 if param == keys.up then
	  -- Up
	  if nHistoryPos == nil then
	   if #_tHistory > 0 then
	    nHistoryPos = #_tHistory
	   end
	  elseif nHistoryPos > 1 then
	   nHistoryPos = nHistoryPos - 1
	  end
	 else
	  -- Down
	  if nHistoryPos == #_tHistory then
	   nHistoryPos = nil
	  elseif nHistoryPos ~= nil then
	   nHistoryPos = nHistoryPos + 1
	  end	 
	 end
	
	 if nHistoryPos then
					 sLine = _tHistory[nHistoryPos]
					 nPos = string.len( sLine )
				    else
	  sLine = ""
	  nPos = 0
	 end
	 redraw()
			    end
   elseif param == keys.backspace then
    -- Backspace
    if nPos > 0 then
	 redraw(" ");
	 sLine = string.sub( sLine, 1, nPos - 1 ) .. string.sub( sLine, nPos + 1 )
	 nPos = nPos - 1	
	 redraw()
    end
   elseif param == keys.home then
    -- Home
    nPos = 0
    redraw() 
   elseif param == keys.delete then
    if nPos < string.len(sLine) then
	 redraw(" ");
	 sLine = string.sub( sLine, 1, nPos ) .. string.sub( sLine, nPos + 2 )   
	 redraw()
    end
   elseif param == keys["end"] then
    -- End
    nPos = string.len(sLine)
    redraw()
   end
  end
end

term.setCursorBlink( false )
term.setCursorPos( w + 1, sy )
print()

return sLine
end
GopherAtl #3
Posted 30 December 2012 - 04:26 AM
read() doesn't support a max length, best you can do is check the length after, ex


function readLen(prompt, minLen,maxLen,isPassword)
  while true do
	write(prompt)
	--//read the line; if isPassword is true, pass "*" as the char to display, otherwise nil so it displays what's typed
	local sInput=read(isPassword and "*" or nil)
	--//if the length is less than min or greater than max...
	if #sInput<minLen or #sInput>maxLen then
	  --//...complain
	  print("Must be between "..minLen.." and "..maxLen.." characters long!")
	else --//not too long or short, return it
	  return sInput
	end
  end
end

--//just call it instead of read, passing the prompt, min and max lengths, and optionally "true" if you're reading a password
local username=readLen("Enter username : ",1,16)
--//declare a var for the password before the loop
local password
--//quick loop
while true do
  password=readLen("Enter password   : ",4,16,true)
  --//local declared inside loop, will go away when the loop ends
  local pw2=readLen("Confirm password : ",4,16,true)

  --//see if they match
  if pw2==password then
	break --//pws match, end the while loop
  end
  print("Passwords don't match! Try again")
end

:edit: or, you could modify the original read function grabie posted, if checking after and forcing re-entry bothers you.
remiX #4
Posted 30 December 2012 - 04:38 AM
All you do is make your own function with os.pullEvent and keep adding the inputted char to a line and if the line is over 10, it doesn't add any more.

I have one somewhere on my pc, i'll try find it now, else I'll make a quick one
exploder #5
Posted 30 December 2012 - 04:39 AM
you have to write custom read function that will not allow you to write if string length is more than specified value

here you have original read function

function read( _sReplaceChar, _tHistory )
term.setCursorBlink( true )
	local sLine = ""
local nHistoryPos = nil
local nPos = 0
	if _sReplaceChar then
  _sReplaceChar = string.sub( _sReplaceChar, 1, 1 )
end

local w, h = term.getSize()
local sx, sy = term.getCursorPos()

local function redraw( _sCustomReplaceChar )
  local nScroll = 0
  if sx + nPos >= w then
   nScroll = (sx + nPos) - w
  end
  
  term.setCursorPos( sx, sy )
  local sReplace = _sCustomReplaceChar or _sReplaceChar
  if sReplace then
   term.write( string.rep(sReplace, string.len(sLine) - nScroll) )
  else
   term.write( string.sub( sLine, nScroll + 1 ) )
  end
  term.setCursorPos( sx + nPos - nScroll, sy )
end

while true do
  local sEvent, param = os.pullEvent()
  if sEvent == "char" then
   sLine = string.sub( sLine, 1, nPos ) .. param .. string.sub( sLine, nPos + 1 )
   nPos = nPos + 1
   redraw()
  
  elseif sEvent == "key" then
	  if param == keys.enter then
	-- Enter
	break
  
   elseif param == keys.left then
	-- Left
	if nPos > 0 then
	 nPos = nPos - 1
	 redraw()
	end
  
   elseif param == keys.right then
	-- Right  
	if nPos < string.len(sLine) then
	 nPos = nPos + 1
	 redraw()
	end
  
   elseif param == keys.up or param == keys.down then
				-- Up or down
	if _tHistory then
	 redraw(" ");
	 if param == keys.up then
	  -- Up
	  if nHistoryPos == nil then
	   if #_tHistory > 0 then
		nHistoryPos = #_tHistory
	   end
	  elseif nHistoryPos > 1 then
	   nHistoryPos = nHistoryPos - 1
	  end
	 else
	  -- Down
	  if nHistoryPos == #_tHistory then
	   nHistoryPos = nil
	  elseif nHistoryPos ~= nil then
	   nHistoryPos = nHistoryPos + 1
	  end	
	 end
	
	 if nHistoryPos then
					 sLine = _tHistory[nHistoryPos]
					 nPos = string.len( sLine )
					else
	  sLine = ""
	  nPos = 0
	 end
	 redraw()
				end
   elseif param == keys.backspace then
	-- Backspace
	if nPos > 0 then
	 redraw(" ");
	 sLine = string.sub( sLine, 1, nPos - 1 ) .. string.sub( sLine, nPos + 1 )
	 nPos = nPos - 1	
	 redraw()
	end
   elseif param == keys.home then
	-- Home
	nPos = 0
	redraw()
   elseif param == keys.delete then
	if nPos < string.len(sLine) then
	 redraw(" ");
	 sLine = string.sub( sLine, 1, nPos ) .. string.sub( sLine, nPos + 2 )  
	 redraw()
	end
   elseif param == keys["end"] then
	-- End
	nPos = string.len(sLine)
	redraw()
   end
  end
end

term.setCursorBlink( false )
term.setCursorPos( w + 1, sy )
print()

return sLine
end

This is a bit too complicated for me :D/> but thanks anyways.


read() doesn't support a max length, best you can do is check the length after, ex


function readLen(prompt, minLen,maxLen,isPassword)
  while true do
	write(prompt)
	--//read the line; if isPassword is true, pass "*" as the char to display, otherwise nil so it displays what's typed
	local sInput=read(isPassword and "*" or nil)
	--//if the length is less than min or greater than max...
	if #sInput<minLen or #sInput>maxLen then
	  --//...complain
	  print("Must be between "..minLen.." and "..maxLen.." characters long!")
	else --//not too long or short, return it
	  return sInput
	end
  end
end

--//just call it instead of read, passing the prompt, min and max lengths, and optionally "true" if you're reading a password
local username=readLen("Enter username : ",1,16)
--//declare a var for the password before the loop
local password
--//quick loop
while true do
  password=readLen("Enter password   : ",4,16,true)
  --//local declared inside loop, will go away when the loop ends
  local pw2=readLen("Confirm password : ",4,16,true)

  --//see if they match
  if pw2==password then
	break --//pws match, end the while loop
  end
  print("Passwords don't match! Try again")
end

:edit: or, you could modify the original read function grabie posted, if checking after and forcing re-entry bothers you.


This will do just fine and isn't too complicated, thanks.
exploder #6
Posted 30 December 2012 - 04:44 AM
All you do is make your own function with os.pullEvent and keep adding the inputted char to a line and if the line is over 10, it doesn't add any more.

I have one somewhere on my pc, i'll try find it now, else I'll make a quick one


If you mean that it doesn't allow more than 10 (or more,less) chars to be imputed and doesn't allow any more than it would be perfect if you could post it.

Thanks.
KaoS #7
Posted 30 December 2012 - 05:18 AM
well I would allow them to type whatever they want to, just put a warning at the top saying maxlen: 10 chars and then use the code


str=(#str>10 and string.sub(str,1,10) or str)

to trim it to 10 chars
remiX #8
Posted 30 December 2012 - 05:20 AM
Finally found it!

I didn't have time to test it after taking it out of an old file of mine, but try it:

x, y = term.getSize()

function limitRead(nLimit, replaceChar)
    term.setCursorBlink(true)
    local cX, cY = term.getCursorPos()
    local rString = ""
    if replaceChar == "" then replaceChar = nil end
    while true do
        local xPos, yPos = term.getCursorPos()
        local event, p1 = os.pullEvent()
        if event == "char" then
            -- Character event
            if #rString + 1 <= nLimit then
                rString = rString .. p1
                if not replaceChar then
                    if not nLimit then
                        write(p1)
                    else
                        if #rString >= nLimit then
                            term.setCursorPos(cX, cY)
                            write(string.sub(rString, #rString - nLimit + 1))
                        elseif #rString < nLimit then
                            write(p1)
                        end
                    end
                else
                    if not nLimit then
                        write(replaceChar)
                    else
                        if #rString >= nLimit then
                            term.setCursorPos(cX, cY)
                            write(string.rep(replaceChar, nLimit))
                        elseif #rString < nLimit then
                            write(replaceChar)
                        end
                    end
                end
            end
        elseif event == "key" and p1 == keys.backspace then
            -- Backspace
            rString = string.sub(rString, 1, #rString-1)
            term.setCursorPos(cX, cY)
            term.clearLine()
            if replaceChar then
                for i = 1, #rString do write(replaceChar) end
            else
                write(rString)
            end
        elseif event == "key" and p1 == keys.enter then
            -- Enter
            break
        end
    end
    term.setCursorBlink(false)
    print() -- Skip to the next line after clicking enter.
    return rString
end

-- And you call it like this
input = limitRead(10)

-- If you want to replace it with a char, like read() then
password = limitRead(10, "*")
Orwell #9
Posted 30 December 2012 - 05:43 AM
I wrote something quickly myself, no moving with left and right arrow like the original read because I'm fealing lazy :P/> (could hardly bother anyone with 10 characters anyway). I think this might be quite the same as the previous snippets, but I focused on making it as understandable as possible.


local function readN(len, replaceChar)
  len = len or 10
  local input=""
  local key = 0
  term.setCursorBlink(true)
  repeat
	local e,p1 = os.pullEvent()
	if e=="char" then
	  if #input < len then
		input = input .. p1
		term.write(replaceChar or p1)
	  end
	elseif e=="key" and p1==keys.backspace and #input > 0 then
	  input = input:sub(1,#input-1)
	  local x,y = term.getCursorPos()
	  term.setCursorPos(x-1,y)
	  term.write(" ")
	  term.setCursorPos(x-1,y)
	end
  until p1==keys.enter
  term.setCursorBlink(false)
  return input
end

Edit: it's not the same as the previous ones except for remiX's. The main difference is that he writes the entire string on each iteration, while I handle each position seperately. Also, for unlimited length, you could just use math.huge or term.getSize() instead of adding seperate conditions. For replaceChar a simple 'or' is sufficient.
exploder #10
Posted 31 December 2012 - 03:10 AM
Finally found it!

I didn't have time to test it after taking it out of an old file of mine, but try it:

x, y = term.getSize()

function limitRead(nLimit, replaceChar)
	term.setCursorBlink(true)
	local cX, cY = term.getCursorPos()
	local rString = ""
	if replaceChar == "" then replaceChar = nil end
	while true do
		local xPos, yPos = term.getCursorPos()
		local event, p1 = os.pullEvent()
		if event == "char" then
			-- Character event
			if #rString + 1 <= nLimit then
				rString = rString .. p1
				if not replaceChar then
					if not nLimit then
						write(p1)
					else
						if #rString >= nLimit then
							term.setCursorPos(cX, cY)
							write(string.sub(rString, #rString - nLimit + 1))
						elseif #rString < nLimit then
							write(p1)
						end
					end
				else
					if not nLimit then
						write(replaceChar)
					else
						if #rString >= nLimit then
							term.setCursorPos(cX, cY)
							write(string.rep(replaceChar, nLimit))
						elseif #rString < nLimit then
							write(replaceChar)
						end
					end
				end
			end
		elseif event == "key" and p1 == keys.backspace then
			-- Backspace
			rString = string.sub(rString, 1, #rString-1)
			term.setCursorPos(cX, cY)
			term.clearLine()
			if replaceChar then
				for i = 1, #rString do write(replaceChar) end
			else
				write(rString)
			end
		elseif event == "key" and p1 == keys.enter then
			-- Enter
			break
		end
	end
	term.setCursorBlink(false)
	print() -- Skip to the next line after clicking enter.
	return rString
end

-- And you call it like this
input = limitRead(10)

-- If you want to replace it with a char, like read() then
password = limitRead(10, "*")
I wrote something quickly myself, no moving with left and right arrow like the original read because I'm fealing lazy :P/> (could hardly bother anyone with 10 characters anyway). I think this might be quite the same as the previous snippets, but I focused on making it as understandable as possible.


local function readN(len, replaceChar)
  len = len or 10
  local input=""
  local key = 0
  term.setCursorBlink(true)
  repeat
	local e,p1 = os.pullEvent()
	if e=="char" then
	  if #input < len then
		input = input .. p1
		term.write(replaceChar or p1)
	  end
	elseif e=="key" and p1==keys.backspace and #input > 0 then
	  input = input:sub(1,#input-1)
	  local x,y = term.getCursorPos()
	  term.setCursorPos(x-1,y)
	  term.write(" ")
	  term.setCursorPos(x-1,y)
	end
  until p1==keys.enter
  term.setCursorBlink(false)
  return input
end

Edit: it's not the same as the previous ones except for remiX's. The main difference is that he writes the entire string on each iteration, while I handle each position seperately. Also, for unlimited length, you could just use math.huge or term.getSize() instead of adding seperate conditions. For replaceChar a simple 'or' is sufficient.


Both of them are working perfect, thanks everyone.

Edit: remiX you have a little problem in your code because if I press backspace no matter where it will erase everything that is behind the input.

Orwell's code doesn't have this bug.
remiX #11
Posted 31 December 2012 - 05:27 AM
Both of them are working perfect, thanks everyone.

Edit: remiX you have a little problem in your code because if I press backspace no matter where it will erase everything that is behind the input.

Orwell's code doesn't have this bug.

Well, like I said I hadn't tested it or used it in a while :)/> But I don't remember that happening to me, haha. I must have screwed something up when copying it.