Posted on by

Today’s tutorial features Lua string manipulation and parsing techniques which can be useful in several applications, especially business or database-powered apps where you need to check and confirm that text is formatted properly. These methods “enhance” the native Lua string library and come to you courtesy of the Lua Users Wiki where you can find various Lua tidbits and code samples.


Splitting Strings

Do you have some data that’s in the form of a long string and you need to break it up into smaller strings based on a custom pattern? A common example is street addresses:

Flintstone, Fred, 101 Rockledge, Bedrock, 98775, 555-555-1212
Rubble, Barney, 103 Rockledge, Bedrock, 98775, 555-555-1313

Now, let’s assume you want to parse out each piece of data separated by the comma. In languages like Perl or PHP, there are functions built into the core that will do that for you. In Perl, it’s the “split” function; in PHP it’s the explode() function.

Lua does not have a “split” function native to its string library, but with a small function, you can include the same functionality. Inspect the following code:

function string:split( inSplitPattern, outResults )

   if not outResults then
      outResults = { }
   end
   local theStart = 1
   local theSplitStart, theSplitEnd = string.find( self, inSplitPattern, theStart )
   while theSplitStart do
      table.insert( outResults, string.sub( self, theStart, theSplitStart-1 ) )
      theStart = theSplitEnd + 1
      theSplitStart, theSplitEnd = string.find( self, inSplitPattern, theStart )
   end
   table.insert( outResults, string.sub( self, theStart ) )
   return outResults
end

This function accomplishes the desired task gracefully, provides a user-defined pattern to split on, and allows for an optional table to append the strings to.

Both methods of calling the function are valid:

local myString = "Flintstone, Fred, 101 Rockledge, Bedrock, 98775, 555-555-1212"

local myTable = myString:split(", ")
for i = 1, #myTable do
   print( myTable[i] )
end
local myString = "Flintstone, Fred, 101 Rockledge, Bedrock, 98775, 555-555-1212"

local myTable = string.split( myString, ", " )
for i = 1, #myTable do
   print( myTable[i] )
end

Trimming Strings

Trimming strings can be useful in several cases, especially if you’re working with data from an external source, or data that is input by the user via the device’s soft keyboard. As with the “split” functionality, string trimming is not native to Lua’s string library, but it can be accomplished easily enough. Inspect the following function:

local function trimString( s )
   return string.match( s,"^()%s*$") and "" or string.match(s,"^%s*(.*%S)" )
end

local untrimmedString = "   Some untrimmed string here.     "
print( "["..untrimmedString.."]" )

local trimmedString = trimString( untrimmedString )
print( "["..trimmedString.."]" )

Format as “Title Case”

Occasionally it’s useful to convert a string into “title case,” in which the first letter of each word is converted to uppercase while the remaining letters are set as lowercase. As an example, let’s assume that a name was entered in a very sloppy format like:

Obi-waN kEnObi

Of course, you probably want to correct this, and doing so requires just a few lines of code:

local function titleCase( first, rest )
   return first:upper()..rest:lower()
end

local characterName = "Obi-waN kEnObi"

characterName = characterName:gsub( "(%a)([%w_']*)", titleCase )
print( characterName )

The result is exactly as expected: Obi-Wan Kenobi.


Strict Line Wrapping

Sometimes you’ll have a long string (paragraph) of text which needs to be line-wrapped at a specific character interval, not a pixel distance. Corona’s display.newText() API handles line wrapping if you provide the width parameter, but if you want interval-specific control, you might prefer the following function which allows you to set a maximum number of characters per line as the limit argument. In addition, it allows you to set indent values — as strings, not integers — for both the start of the paragraph and/or each following line.

local function textWrap( str, limit, indent, indent1 )

   limit = limit or 72
   indent = indent or ""
   indent1 = indent1 or indent

   local here = 1 - #indent1
   return indent1..str:gsub( "(%s+)()(%S+)()",
      function( sp, st, word, fi )
         if fi-here > limit then
            here = st - #indent
            return "\n"..indent..word
         end
      end )
end

local initialText = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet."

local wrappedText = textWrap( initialText, 36, nil, "    " )

print( wrappedText )

local textBox = display.newRect( 30, 30, 640, 840 ) ; textBox:setFillColor( 200, 80, 50, 50 )
      textBox.strokeWidth = 4 ; textBox:setStrokeColor( 200, 80, 50, 150 )

local myParagraph = display.newText( wrappedText, 66, 58, 580, 800, native.systemFont, 28 )

Let’s examine the arguments in order:

  • str (string; required) — the string of text which you want to line-wrap
  • limit (integer; optional) — the maximum interval at which to wrap the lines
  • indent (string; optional) — indention string for each line, for example “[2 spaces]”
  • indent1 (string; optional) — indention string for the first line only, for example “[6 spaces]”

Another benefit of this function versus auto-wrapping is that you can “rewrap” the text to a new interval without changing or recreating the newText display object itself. The following example rewraps the same text to an interval limit of 16 instead of the initial limit of 36. Of course, you can also change the text entirely and/or adjust the indent values.

myParagraph.text = textWrap( initialText, 16, "", "    " )

Confirm Email Format

Another useful string check is confirming that a user has entered an email address in the proper format. Of course, this function can’t check if the actual address is valid and functional, but at least it confirms the format.

local email = "username@domain.com"

if ( email:match("[A-Za-z0-9%.%%%+%-]+@[A-Za-z0-9%.%%%+%-]+%.%w%w%w?%w?") ) then
   print( email.." IS formatted properly." )
else
   print( email.." is NOT formatted properly." )
end

In Summary…

That wraps up today’s tutorial on Lua string “magic.” Hopefully you can find a place for these in your apps! As usual, please post your comments and questions below.


Posted by . Thanks for reading...

6 Responses to “Tutorial: Lua String Magic”

  1. Mustafa A.

    Nice tutorial Rob and very useful techniques! Please add more in future posts.

    I have a Lua question, let’s assume we have this string:

    local initialText = “Lorem ipsum dolor sit amet consetetur”

    How can I trim the string based on number of character letters?

    So if limit is 11, then it will be like this

    local finalText = “Lorem ipsum”

    Thank you,
    Mustafa A.

    Reply
      • Mustafa A.

        Thank you Rob,

        It worked but I get a weird question mark at the end of the string after it’s trimmed.

        Is it because I’m using Arabia characters? Is it some kind of unicode porblem?

        noon[i] = tableOnDemand.posts[i].title
        noon[i] = noon[i]:sub(1,50) . . “…”
        featuredTileTitle[i] = display.newText(noon[i], 0, 0, 180, 100, native.systemFontBold, 16)

        Any idea?

        Reply
  2. Suhada

    Thanks for another great tutorial.
    I’d love to see a tutorial taking this theme of “business or database-powered apps” further – describing how to allow user input of a range of variables:
    Inputting some strings, some numbers, some options, preferably enough to mean scrolling is necessary and preferably something which will also run on the simulator too.

    Reply
  3. Steve Taylor

    Roland, very nice looking library, I was able to look at the code and get a pretty good idea of what it did. I selected the documentation link and got a 404 error, should I plan on just using the code for docs or was there just an error in setting up the docs? Thanks for the nice library!

    Reply

Leave a Reply

  • (Will Not Be Published)