Quiloos39
Quiloos39

Reputation: 57

Lua string pattern

Here is my code:

local code = [[
    local a = 1

    local b = 2
]]

local y = 0

for Paragraph in string.gmatch(code,"[^\n]+") do
    print(Paragraph)
    for Word in string.gmatch(Paragraph, "[^ ]+") do        

    end
    y = y + 1
end

The problem is pattern doesn't recognize my empty paragraphs how can i fix this ?

If you run the code you will what i mean

Output is:

local a = 1
local b = 2

Instead it should be:

--space
local a = 1
--space
local b = 2
--space

Upvotes: 1

Views: 609

Answers (3)

Nick Gammon
Nick Gammon

Reputation: 1171

Instead it should be:

    --space
    local a = 1

You won't get that first space because Lua long strings discard the first newline.


Your problem is that your match on "[^\n]+" matches at least one character which is not a newline. An empty line won't match (there are no characters between the newlines) and thus they don't get shown.

Now you can change that to "[^\n]*" like this:

for Paragraph in string.gmatch(code,"[^\n]*") do
    print("Line=", Paragraph)
    for Word in string.gmatch(Paragraph, "[^ ]+") do        
      print ("Word=", Word)
    end
end

But that has a different problem:

Line=     local a = 1
Word= local
Word= a
Word= =
Word= 1
Line= 
Line= 
Line=     local b = 2
Word= local
Word= b
Word= =
Word= 2
Line= 
Line= 

Blank lines appear twice!


A handy function for iterating through a string, a line at a time, is this:

function getlines (str)

  local pos = 0

  -- the for loop calls this for every iteration
  -- returning nil terminates the loop
  local function iterator (s)

    if not pos then
      return nil
    end -- end of string, exit loop

    local oldpos = pos + 1  -- step past previous newline
    pos = string.find (s, "\n", oldpos) -- find next newline

    if not pos then  -- no more newlines, return rest of string
      return string.sub (s, oldpos)
    end -- no newline

    return string.sub (s, oldpos, pos - 1)

  end -- iterator

  return iterator, str
end -- getlines

That handles empty lines. Now you can write your code like this (assuming the function above precedes your code):

for Paragraph in getlines (code) do
    print("Line=", Paragraph)
    for Word in string.gmatch(Paragraph, "[^ ]+") do        
      print ("Word=", Word)
    end
end

Output:

Line=     local a = 1
Word= local
Word= a
Word= =
Word= 1
Line= 
Line=     local b = 2
Word= local
Word= b
Word= =
Word= 2
Line= 

Make a Lua module

You can turn the function getlines into a Lua module, like this:

getlines.lua

function getlines (str)

  local pos = 0

  -- the for loop calls this for every iteration
  -- returning nil terminates the loop
  local function iterator (s)

    if not pos then
      return nil
    end -- end of string, exit loop

    local oldpos = pos + 1  -- step past previous newline
    pos = string.find (s, "\n", oldpos) -- find next newline

    if not pos then  -- no more newlines, return rest of string
      return string.sub (s, oldpos)
    end -- no newline

    return string.sub (s, oldpos, pos - 1)

  end -- iterator

  return iterator, str
end -- getlines

return getlines

Now all you have to do is "require" it:

require "getlines"
for Paragraph in getlines (code) do
    print("Line=", Paragraph)
    for Word in string.gmatch(Paragraph, "[^ ]+") do        
      print ("Word=", Word)
    end
end

Upvotes: 1

Piglet
Piglet

Reputation: 28994

You don't get the empty line because you look for the complement of \n. If you only have \n in your line, there is no complement and you get no match.

You could use this pattern: "[^\n]*\n?" to get what you want. This pattern matches any line. So everything or nothing that is not a \n which is followed by 0 or 1 instances of \n

Upvotes: 1

EinsteinK
EinsteinK

Reputation: 755

This should do the trick:

local y,code = 0,[[

local a = 123

local b = 456


local c = "too much newlines because we're cool"
]]

local i = 1
-- Why would you start with newlines? Oh well
local sigh = code:match("\n+")
y = y + #sigh
-- debug printing of newlines
for i=1,y do print() end
while true do
    local start,stop = code:find("[^\n]+",i)
    if not start then break end
    local Paragraph = code:sub(start,stop)

    -- Do your Paragraph parsing and stuff
    print("PARAGRAPH:",Paragraph)

    start,stop = code:find("\n+",stop+1)
    if not stop then break end
    y,i = y + stop - start + 1,stop+1
    -- printing newlines to get the desired effect in output
    for i=2,stop-start+1 do print() end
    -- starting from 2 since the print(code:sub(...)) already prints one \n
end
-- reached end of the string

It gives you this nice output:

PARAGRAPH:      local a = 123

PARAGRAPH:      local b = 456


PARAGRAPH:      local c = "too much newlines because we're cool"

Tested at http://www.lua.org/cgi-bin/demo

Upvotes: 0

Related Questions