Finding the position of a word within a string (string. library)

tl;dr: I’m trying to figure out if I can use string.gmatch to return the start location of words.

ie, this example from the Lua manual:

s = "hello world from Lua. " for w in string.gmatch(s, "%a+") do print(w) end
…Each word is returned. Presumably there is a way to also return the start value as you would get using string.find(), but I can’t seem to find anything on returning that value using the control codes (eg: “%a+” in that example)

Details : I’m trying to build a function who’s job is only to look through a string, and if it exceeds a given length, insert a line break. But it has to be smart enough to insert the linebreak before or after a word, not inbetween.

display.newText() does do text wrapping, but it’s simply looking at the width of the object at the time, so in my case (typing out words letter by letter), the autowrap function would simply go midway through a word, realize the next character would take it too far, and then teleport the word to the bottom. I’m trying to write something that looks through the string ahead of time and removes that possibility by inserting \n line breaks.

local limit = 25 -- max number of characters per line if wordstart + wordlength \> limit then --insert linebreak at wordstart end

Something like that, only with an iterator so I can add 2nd and 3rd linebreaks as necessary… [import]uid: 41884 topic_id: 29743 reply_id: 329743[/import]

Hi Richard,
I’d suggest starting a basic counter at 0 and “reformatting” your entire string, doing a .gmatch loop for each word. The process might go like this:

  1. counter variable begins at 0; likewise, “newString” begins as just “”
  2. find the first word (gmatch)
  3. append that word to newString
  4. check that word’s length and add to counter (+1 for a space)
  5. if counter is less than max, proceed to next word, ELSE append newline character to newString and reset counter to 0, then proceed in the loop.

So basically, it’s about using gmatch to loop through your original string and “build” a new string with line breaks in the proper places. The new string would be the one displayed when all was complete.

Hope this helps!
Brent

[import]uid: 9747 topic_id: 29743 reply_id: 119413[/import]

I posted a comment in this thread; take a look at some of the standard ways of doing this:

https://developer.coronalabs.com/code/simple-string-split-function-method

Many standard lua functions return position indices, including gsub… [import]uid: 8271 topic_id: 29743 reply_id: 119435[/import]

Brent, your flow wouldn’t work in this case.

  1. The nature of the problem is such that you need to know the length before you append - it’s too late to add a line break if you’ve already busted line length. (I guess it’s kinda like Blackjack!)

  2. My experience with gmatch is limited but I don’t see a way to do a single check with it; since it returns an iterator function I seemingly (?) have no choice but to parse out the string ahead of time.

Nonetheless, it gave me exactly the push in the right direction I needed to figure it out. Here’s the code I came up with:

-- FUNCTION: Examines a string and inserts linebreaks if the string would exceed the message window parameters.  
function addLineBreaks(script, limit)  
  
 -- Find the string length.  
 local length = string.len(script)  
  
 if length \> 0 then  
  
 print("converting...")  
 local limit = limit or 25 -- the limit to the length of 1 line.  
 local counter = { word=1, line=0 }  
 local newString = ""  
  
 -- Extract the words from the string.  
 local words = {}  
 for word in string.gmatch(script, "%S+") do  
 words[#words+1] = word  
 end  
  
 -- Loop to create the new string.  
 while counter.word \< #words+1 do  
  
 -- Localize the next word to add.  
 local word = words[counter.word]  
  
 -- Check the word's length  
 local wordlength = string.len(word)  
  
 -- If the word puts the line over the limit,  
 if counter.line + wordlength \> limit then  
  
 -- Append the line break first  
 newString = newString.."\n"  
  
 -- Reset the line counter  
 counter.line = 0  
  
 end  
  
 -- Append the word to the new string  
 newString = newString.." "..word  
  
 -- Update the line counter and string counter and word counter  
 counter.word = counter.word + 1  
 counter.line = counter.line + wordlength + 1  
  
 -- Clean up  
 wordlength = nil  
  
 end  
  
 -- Clean up  
 table.remove(counter)  
 counter = nil  
 return newString  
 end  
end  
  
local test = "My cat can fight your dog and win anyday. I'm sure of it."  
local result = addLineBreaks(test, 25)  
-- My cat can fight your dog  
-- and win anyday. I'm sure  
-- of it.  

I can add that to the repository later; just not convinced it isn’t sloppy. (strings are proving to be a lot more complex than most of the corona commandset…) Seems to work fine so far, anyway (I’m passing it through a typing parser, sort of like in old RPGs where the text characters type out onscreen)
[import]uid: 41884 topic_id: 29743 reply_id: 119436[/import]

horace, that function is interesting enough that I’ll stare at it for a bit. Gotta admit it’s really not descriptive of how it works/what the advantage is versus, say, the gmatch gather for loop in my function, but that’s mostly because if you show me a gmatch pattern I see a whole bunch of nonsense. :wink:

edit: ah, returns indices and words seperately. interesting. Not directly applicable but definitely useful elsewhere! [import]uid: 41884 topic_id: 29743 reply_id: 119437[/import]

Yep, I wrote that concept well after midnight so I’m not surprised it’s a bit off. Nonetheless, good to hear you came up with a solution. After you test it more thoroughly, would you consider adding it to the Code Share library? I’m sure other developers have faced this dilemma before and would find your function(s) useful!

Brent
[import]uid: 9747 topic_id: 29743 reply_id: 119447[/import]

I ditto to Brent @Ignis Design – I mean, I’d be so interested in this function. @richard9, please do add it to the code share when you feel it’s ready.

Cheers,
Naomi [import]uid: 67217 topic_id: 29743 reply_id: 119457[/import]