SkarXa
SkarXa

Reputation: 1194

Regex to match text between commas

I'm going nuts trying to get a regex to detect spam of keywords in the user inputs. Usually there is some normal text at the start and the keyword spam at the end, separated by commas or other chars.

What I need is a regex to count the number of keywords to flag the text for a human to check it.

The text is usually like this:

[random text, with commas, dots and all]

keyword1, keyword2, keyword3, keyword4, keyword5,
Keyword6, keyword7, keyword8...

I've tried several regex to count the matches:

-This only gets one out of two keywords

[,-](\w|\s)+[,-]

-This also matches the random text

(?:([^,-]*)(?:[^,-]|$))

Can anyone tell me a regex to do this? Or should I take a different approach?

Thanks!

Upvotes: 9

Views: 43618

Answers (8)

ofsahin
ofsahin

Reputation: 1

If people still search for this in 2021

([^,\n])+

Match anything except new line and comma regexr.com/60eme

Upvotes: 0

Code Novice
Code Novice

Reputation: 2398

How to match on the String of text between the commas?

This SO Post was marked as a duplicate to my posted question however since it is NOT a duplicate and there were no answers in THIS SO Post that answered my question on how to also match on the strings between the commas see below on how to take this a step further.

How to Match on single digit values in a CSV String

For example if the task is to search the string within the commas for a single 7, 8 or a single 9 but not match on combinations such as 17 or 77 or 78 but only the single 7s, 8s, or 9s see below...

The answer is to Use look arounds and place your search pattern within the look arounds:

(?<=^|,)[789](?=,|$)

See live demo.

The above Pattern is more concise however I've pasted below the Two Patterns provided as solutions to THIS this question of matching on Strings within the commas and they are:

(?<=^|,)[789](?=,|$) Provided by @Bohemian and chosen as the Correct Answer

(?:(?<=^)|(?<=,))[789](?:(?=,)|(?=$)) Provided in comments by @Ouroborus

Demo: https://regex101.com/r/fd5GnD/1

Upvotes: 1

Wiktor Stribiżew
Wiktor Stribiżew

Reputation: 627409

A regex that will match strings between two commas or start or end of string is

(?<=,|^)[^,]*(?=,|$)

Or, a bit more efficient:

(?<![^,])[^,]*(?![^,])

See the regex demo #1 and demo #2.

Details:

  • (?<=,|^) / (?<![^,]) - start of string or a position immediately preceded with a comma
  • [^,]* - zero or more chars other than a comma
  • (?=,|$) / (?![^,]) - end of string or a position immediately followed with a comma

Upvotes: 1

Taemyr
Taemyr

Reputation: 3437

Pr your answer to my question, here is a regexp to match a string that occurs between two commas.

(?<=,)[^,]+(?=,)

This regexp does not match, and hence do not consume, the delimiting commas. This regexp would match " and hence do not consume" in the previous sentence.

The fact that your regexp matched and consumed the commas was the reason why your attempted regexp only matched every other candidate.

Also if the whole input is a single string you will want to prevent linebreaks. In that case you will want to use;

(?<=,)[^,\n]+(?=,)

http://www.phpliveregex.com/p/1DJ

Upvotes: 17

Steven
Steven

Reputation: 6148

As others have said this is potentially a very tricky thing to do... It suffers from all of the same failures as general "word filtering" (e.g. people will "mask" the input). It is made even more difficult without plenty of example posts to test against...

Solution

Anyway, assuming that keywords will be on separate lines to the rest of the input and separated by commas you can match the lines with keywords in like:

Regex

#(?:^)((?:(?:[\w\.]+)(?:, ?|$))+)#m

Input

Taken from your question above:

[random text, with commas, dots and all]

keyword1, keyword2, keyword3, keyword4, keyword5,
Keyword6, keyword7, keyword8

Output

// preg_match_all('#(?:^)((?:(?:[\w]+)(?:, ?|$))+)#m', $string, $matches);
// var_dump($matches);

array(2) {
  [0]=>
  array(2) {
    [0]=>
    string(49) "keyword1, keyword2, keyword3, keyword4, keyword5,"
    [1]=>
    string(31) "Keyword6, keyword7, keyword8..."
  }
  [1]=>
  array(2) {
    [0]=>
    string(49) "keyword1, keyword2, keyword3, keyword4, keyword5,"
    [1]=>
    string(31) "Keyword6, keyword7, keyword8"
  }
}

Explanation

#(?:^)((?:(?:[\w]+)(?:, ?|$))+)#m
  1. # => Starting delimiter
  2. (?:^) => Matches start of line in a non-capturing group (you could just use ^ I was using |\n originally and didn't update)
  3. ( => Start a capturing group
  4. (?: => Start a non-capturing group
  5. (?:[\w]+) => A non-capturing group to match one or more word characters a-zA-Z0-9_ (Using a character class so that you can add to it if you need to....)
  6. (?:, ?|$) => A non-capturing group to match either a comma (with an optional space) or the end of the string/line
  7. )+ => End the non-capturing group (4) and repeat 5/6 to find multiple matches in the line
  8. ) => Close the capture group 3
  9. # => Ending delimiter
  10. m => Multi-line modifier

Follow up from number 2:

#^((?:(?:[\w]+)(?:, ?|$))+)#m

Counting keywords

Having now returned an array of lines only containing key words you can count the number of commas and thus get the number of keywords

$key_words = implode(', ', $matches[1]); // Join lines returned by preg_match_all
echo substr_count($key_words, ',');      // 8

N.B. In most circumstances this will return NUMBER_OF_KEY_WORDS - 1 (i.e. in your case 7); it returns 8 because you have a comma at the end of your first line of key words.


Links

http://php.net/manual/en/reference.pcre.pattern.modifiers.php
http://www.regular-expressions.info/
http://php.net/substr_count

Upvotes: 3

GordonM
GordonM

Reputation: 31770

Why not just use explode and trim?

$keywords = array_map ('trim', explode (',', $keywordstring));

Then do a count() on $keywords.

If you think keywords with spaces in are spam, then you can iterate of the $keywords array and look for any that contain whitespace. There might be legitimate reasons for having spaces in a keyword though. If you're talking about superheroes on your system, for example, someone might enter The Tick or Iron Man as a keyword

I don't think counting keywords and looking for spaces in keywords are really very good strategies for detecting spam though. You might want to look into other bot protection strategies instead, or even use manual moderation.

Upvotes: 1

Jeroen
Jeroen

Reputation: 1002

I think the difficulty is that the random text can also contain commas.

If the keywords are all on one line and it is the last line of the text as a whole, trim the whole text removing new line characters from the end. Then take the text from the last new line character to the end. This should be your string containing the keywords. Once you have this part singled out, you can explode the string on comma and count the parts.

<?php
$string = " some gibberish, some more gibberish, and random text

keyword1, keyword2, keyword3

";

$lastEOL = strrpos(trim($string), PHP_EOL);
$keywordLine = substr($string, $lastEOL);
$keywords = explode(',', $keywordLine);

echo "Number of keywords: " . count($keywords);

I know it is not a regex, but I hope it helps nevertheless.

The only way to find a solution, is to find something that separates the random text and the keywords that is not present in the keywords. If a new line is present in the keywords, you can not use it. But are 2 consecutive new lines? Or any other characters.

$string = " some gibberish, some more gibberish, and random text

keyword1, keyword2, keyword3,
keyword4, keyword5, keyword6,
keyword7, keyword8, keyword9

";

$lastEOL = strrpos(trim($string), PHP_EOL . PHP_EOL); // 2 end of lines after random text
$keywordLine = substr($string, $lastEOL);
$keywords = explode(',', $keywordLine);

echo "Number of keywords: " . count($keywords);

(edit: added example for more new lines - long shot)

Upvotes: -4

MC ND
MC ND

Reputation: 70941

Your first regexp doesn't need a preceding comma

[\w\s]+[,-]

Upvotes: 0

Related Questions