• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 597
  • Last Modified:

PHP/REGEX: Put each line inside <li> tags

Using PHP and REGEX, I want to every item that is on its own line inside <li> tags.

This:
Hello
123
ABC

Open in new window


Should become this:
<li>Hello</li>
<li>123</li>
<li>ABC</li>

Open in new window

0
hankknight
Asked:
hankknight
  • 7
  • 6
  • 4
  • +1
3 Solutions
 
Julian HansenCommented:
There are different ways of doing this depending on where the lines come from.
Here is one way but I don't think this is what you were asking
foreach($lines as $l) {
 echo '<li>' . $l . '</li>'
}

Open in new window

Where does the data come from - what form is it in?
Hullo
123
ABC

Open in new window

0
 
käµfm³d 👽Commented:
Yes, please provide an example of how the data is stored in your code. If it is a string object with embedded newlines, then it seems as though a simple split would suffice (rather than a regex).

e.g.

$input = <<<INPUT
Hello
123
ABC
INPUT;

// tweaking julianH's example
foreach(explode('\n', $input) as $l) {
 echo '<li>' . $l . '</li>'
}

Open in new window

0
 
Julian HansenCommented:
or
// tweaking kaufmed's example
echo '<li>' . str_replace('\n', '</li><li>', $input) . '</li>;

Open in new window

0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
hankknightAuthor Commented:
Can this be done with preg_replace?
0
 
Julian HansenCommented:
yes but why would you want to ? Nothing you have posted indicates the need for a regular expression - hence our questions trying to find out more about your input.
0
 
hankknightAuthor Commented:
My input will be a textarea.  I am worried that depending on their oporating system, using '\n' will not work and that is why I would rather use REGEX.
0
 
Julian HansenCommented:
REGEX is short regular expression and it would still need something to match on.

The only potential difference in linebreaks is \n or \r\n so you can prefix the above with

$input = str_replace('\r\n','\n', $input)

To standardise (although this is most likely not necessary) and then run the result throug either my or kaufmed's sample above.

I use the above on my CMS for lists - clients don't want to know about <li></li> so I let them put in a \n delimited text area and do the following
  echo '<li>' . str_replace('\n', '</li><li>', $input) . '</li>;

Open in new window

0
 
käµfm³d 👽Commented:
The only potential difference in linebreaks is \n or \r\n so you can prefix the above with
I take issue with that. One would hope that would be the case, but it is possible that someone (either knowingly or unknowingly) could put simply a "\r". For that reason, I would use a pattern of:

$input = preg_replace('/^(.*)(\r?)$/','<li>$1</li>$2', $input);

Open in new window

0
 
Julian HansenCommented:
@kaufmed from a purist view I agree with you however consider the code below

If we miss a \r or two - that counts as white space and will be ignored within the list anyway so net effect (in this case) should be nil.
However, if it is really an issue then the solution is just to prefix the conversion with this

$input = str_replace("\r\n","\n", $input);

Which should sort it out
<!doctype html>
<html>
<head>
<title>Title</title>
</head>
<body>
<ul>
<?php

$input = "abc\r\n123\r\nghi";

echo '<li>' . str_replace("\n", '</li><li>', $input) . '</li>';
?>
</ul>
</body>
</html>

Open in new window

Some things to note (and I made this mistake in posts above).

Single quoted strings in PHP do not result in escaped characters being converted double quotes are required.

Also a preg_replace is going to be slightly more expensive than a straight string replace which is also simpler to write / read in this case.
0
 
hankknightAuthor Commented:
This does not work:
<?php

$input ="Hello
ABC
Test
123";

echo '<ul>' . preg_replace('/^(.*)(\r?)$/','<li>$1</li>$2', $input) . '</ul>';

?>

Open in new window

0
 
hankknightAuthor Commented:
Actually, neither of the proposed ideas work for me:

<?php

$input ="Hello
ABC
Test
123";

echo '<ul>' . preg_replace('/^(.*)(\r?)$/','<li>$1</li>$2', $input) . '</ul>';
echo '<hr />';
echo '<ul>' . '<li>' . str_replace('\n', '</li><li>', $input) . '</li>' . '</ul>';

?> 

Open in new window

0
 
Julian HansenCommented:
@hankknight
Actually, neither of the proposed ideas work for me:

Any particular reason why?

Did you see the comment I made at the bottom of my last post regarding single vs double quotes
<?php

$input ="Hello
ABC
Test
123";

echo '<ul>' . preg_replace('/^(.*)(\r?)$/','<li>$1</li>$2', $input) . '</ul>';
echo '<hr />';
echo '<ul>' . '<li>' . str_replace("\n", '</li><li>', $input) . '</li>' . '</ul>';

?> 

Open in new window


\n and \r\n must be inside "" to work.
0
 
käµfm³d 👽Commented:
Sorry, I forgot a flag. Add the "m" modifier:

echo '<ul>' . preg_replace('/^(.*)(\r?)$/m','<li>$1</li>$2', $input) . '</ul>';

Open in new window

0
 
Terry WoodsIT GuruCommented:
@kaufmed, you'll need some double quotes, rather than single (-:
echo '<ul>' . preg_replace("/^(.*)(\r?)$/m",'<li>$1</li>$2', $input) . '</ul>';

Open in new window

0
 
käµfm³d 👽Commented:
Terry,

It seems to work for me:

Screenshot
0
 
Terry WoodsIT GuruCommented:
The \r isn't matching. You'll get the same output with:
echo '<ul>' . preg_replace('/^(.*)$/m','<li>$1</li>', $input) . '</ul>';

Open in new window

0
 
käµfm³d 👽Commented:
Sorry Terry, but I'm still confused. Even when I use the following data:

Screenshot
I get the desired behavior. (I'm sure we're digressing at this point, but you've piqued my curiosity.) The intent is to match carriage returns, and the only reason I decided to include the carriage return check was to keep it with the newline after the replacement--though in the grand scheme of things it probably doesn't matter. So what am I missing?
0
 
Terry WoodsIT GuruCommented:
Here's a solution:
preg_replace("/([^\r\n]+)(\r*\n\r*|\r|$)/","<li>$1</li>\n", $input) . '</ul>';

Open in new window


@kaufmed, will elaborate in a moment...
0
 
Terry WoodsIT GuruCommented:
TL;DR

@kaufmed, the (.*) part of your pattern captured any \r characters anyway, so the \r part of the pattern wasn't used. I initially thought the \r would be interpreted literally, but I forgot about the additional layer of parsing that the regex engine does. That's why "\\r" to a regex is the same as "\r" and '\r' I believe.

Also, the multiline mode meant that either an end-of-string or a newline was required for each match, which doesn't work when you just have line feeds.

Anyway, here's some code for testing:
<?php

$inputs = array(
  "Just newline"=>"Hello\nABC\nTest\n123",
  "Just line feed"=>"Hello\rABC\rTest\r123",
  "Line feed and newline"=>"Hello\r\nABC\r\nTest\r\n123"
);

foreach ($inputs as $desc=>$data) {
  print "<b>$desc:</b><br>\n";
  print '@kaufmed\'s pattern result:<ul>' .
    preg_replace('/^(.*)(\r?)$/m','<li>$1</li>$2', $data) . '</ul>';
  print '@TerryAtOpus\' pattern result:<ul>' .
    preg_replace("/([^\r\n]+)(\r*\n\r*|\r|$)/","<li>$1</li>\n", $data) . '</ul>';
}

Open in new window


And the result:
screenshot
0
 
käµfm³d 👽Commented:
the (.*) part of your pattern captured any \r characters anyway
Agreed (and embarrassed that I forgot about my greedines!).

I reviewed the PHP string documentation and I see that single-quoted strings do not interpret escape characters, so I concur with your assessment.
0
 
Julian HansenCommented:
Not sure why this is getting so complicated - as discussed above - if a \r or \n is missed it counts as whitespace so you will get the desired result - in terms of how it is used.

Using preg_replace will work but a bit complicated for what should really be a very simple problem which is to put <li></li> tags around each of the items in the list.
0

Featured Post

Free Tool: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 7
  • 6
  • 4
  • +1
Tackle projects and never again get stuck behind a technical roadblock.
Join Now