I am trying to create the following regular expression in PHP to match the following:
[2013-01-01 12:34:12.123] [USERNAME] something
I am able to get part of the first part but I am new to regex in php, any help is appreciated.
Important note: any space above could be one space or more.
(This is what I got so far)
/^[\[][0-9]{4}-[0-9]{2}-[0-9]{2}]/
I am using this tool to test that my regex match: http://www.pagecolumn.com/tool/pregtest.htm (just want to confirm that it is an okay tool).
Update: to clerify more, something could be any amount of text, white spaces above could be any amount of white spaces and USERNAME could be any amount of text as well.
\[\d{4}-\d{2}-\d{2}\s+[\d:.]+\]\s+\[\w+\]\s+something
http://rubular.com/r/BPGvFN4kwi
You're not specific about your rules. For example the very first part probably needs to be a date, but the regex can match 13 for the month. Is that okay? Also what makes a valid "USERNAME" or "something?"
/^\[([0-9]{4}-[0-9]{2}-[0-9]{2})\s+([0-9]+:[0-9]+:[0-9]+(?:\.[0-9]+)?)+\]\s+\[([^\]]+)\]\s+(.+)/
With comments:
/^
\[ # "[" is a special char and should be escape
([0-9]{4}-[0-9]{2}-[0-9]{2}) # Use brackets for group and capture (see $matches in php function)
\s+ # One or move space chars (space, tab, etc.)
([0-9]+:[0-9]+:[0-9]+(?:\.[0-9]+)?)+ # "(?: )" is a group without capturing
\]
\s+
\[([^\]]+)\] # "[^\]]+" - one or more any char except "]"
\s+
(.+) # One or more any char
/x
P.S.: you can use "\d" instead of "[0-9]" and (in this case; for flexibility) you can use "+" ("one or more char" specifier) instead of "{4}" or "{2}".
P.P.S.: http://www.pagecolumn.com/tool/pregtest.htm contains bug (incorrect handles backslash), try another service.
Since your format has delimiters (the []s), you don't need the checks that the other answers provide. Instead you can simply use
\[([^\]]*)\]\s+\[([^\]]*)\]\s+(.*)
which breaks down to
\[([^\]]*)\] // Capture all non-] chars within [ and ]; this is the date
\s+ // some space
\[([^\]]*)\] // Capture all non-] chars within [ and ] again; this is USERNAME
\s+ // some space
(.*) // Capture all the text after; this is something
You can walk through this regex step-by-step using Debuggex.
Related
I want to design an expression for not allowing whitespace at the beginning and at the end of a string, but allowing in the middle of the string.
The regex I've tried is this:
\^[^\s][a-z\sA-Z\s0-9\s-()][^\s$]\
This should work:
^[^\s]+(\s+[^\s]+)*$
If you want to include character restrictions:
^[-a-zA-Z0-9-()]+(\s+[-a-zA-Z0-9-()]+)*$
Explanation:
the starting ^ and ending $ denotes the string.
considering the first regex I gave, [^\s]+ means at least one not whitespace and \s+ means at least one white space. Note also that parentheses () groups together the second and third fragments and * at the end means zero or more of this group.
So, if you take a look, the expression is: begins with at least one non whitespace and ends with any number of groups of at least one whitespace followed by at least one non whitespace.
For example if the input is 'A' then it matches, because it matches with the begins with at least one non whitespace condition. The input 'AA' matches for the same reason. The input 'A A' matches also because the first A matches for the at least one not whitespace condition, then the ' A' matches for the any number of groups of at least one whitespace followed by at least one non whitespace.
' A' does not match because the begins with at least one non whitespace condition is not satisfied. 'A ' does not matches because the ends with any number of groups of at least one whitespace followed by at least one non whitespace condition is not satisfied.
If you want to restrict which characters to accept at the beginning and end, see the second regex. I have allowed a-z, A-Z, 0-9 and () at beginning and end. Only these are allowed.
Regex playground: http://www.regexr.com/
This RegEx will allow neither white-space at the beginning nor at the end of your string/word.
^[^\s].+[^\s]$
Any string that doesn't begin or end with a white-space will be matched.
Explanation:
^ denotes the beginning of the string.
\s denotes white-spaces and so [^\s] denotes NOT white-space. You could alternatively use \S to denote the same.
. denotes any character expect line break.
+ is a quantifier which denote - one or more times. That means, the character which + follows can be repeated on or more times.
You can use this as RegEx cheat sheet.
In cases when you have a specific pattern, say, ^[a-zA-Z0-9\s()-]+$, that you want to adjust so that spaces at the start and end were not allowed, you may use lookaheads anchored at the pattern start:
^(?!\s)(?![\s\S]*\s$)[a-zA-Z0-9\s()-]+$
^^^^^^^^^^^^^^^^^^^^
Here,
(?!\s) - a negative lookahead that fails the match if (since it is after ^) immediately at the start of string there is a whitespace char
(?![\s\S]*\s$) - a negative lookahead that fails the match if, (since it is also executed after ^, the previous pattern is a lookaround that is not a consuming pattern) immediately at the start of string, there are any 0+ chars as many as possible ([\s\S]*, equal to [^]*) followed with a whitespace char at the end of string ($).
In JS, you may use the following equivalent regex declarations:
var regex = /^(?!\s)(?![\s\S]*\s$)[a-zA-Z0-9\s()-]+$/
var regex = /^(?!\s)(?![^]*\s$)[a-zA-Z0-9\s()-]+$/
var regex = new RegExp("^(?!\\s)(?![^]*\\s$)[a-zA-Z0-9\\s()-]+$")
var regex = new RegExp(String.raw`^(?!\s)(?![^]*\s$)[a-zA-Z0-9\s()-]+$`)
If you know there are no linebreaks, [\s\S] and [^] may be replaced with .:
var regex = /^(?!\s)(?!.*\s$)[a-zA-Z0-9\s()-]+$/
See the regex demo.
JS demo:
var strs = ['a b c', ' a b b', 'a b c '];
var regex = /^(?!\s)(?![\s\S]*\s$)[a-zA-Z0-9\s()-]+$/;
for (var i=0; i<strs.length; i++){
console.log('"',strs[i], '"=>', regex.test(strs[i]))
}
if the string must be at least 1 character long, if newlines are allowed in the middle together with any other characters and the first+last character can really be anyhing except whitespace (including ##$!...), then you are looking for:
^\S$|^\S[\s\S]*\S$
explanation and unit tests: https://regex101.com/r/uT8zU0
This worked for me:
^[^\s].+[a-zA-Z]+[a-zA-Z]+$
Hope it helps.
How about:
^\S.+\S$
This will match any string that doesn't begin or end with any kind of space.
^[^\s].+[^\s]$
That's it!!!! it allows any string that contains any caracter (a part from \n) without whitespace at the beginning or end; in case you want \n in the middle there is an option s that you have to replace .+ by [.\n]+
pattern="^[^\s]+[-a-zA-Z\s]+([-a-zA-Z]+)*$"
This will help you accept only characters and wont allow spaces at the start nor whitespaces.
This is the regex for no white space at the begining nor at the end but only one between. Also works without a 3 character limit :
\^([^\s]*[A-Za-z0-9]\s{0,1})[^\s]*$\ - just remove {0,1} and add * in order to have limitless space between.
As a modification of #Aprillion's answer, I prefer:
^\S$|^\S[ \S]*\S$
It will not match a space at the beginning, end, or both.
It matches any number of spaces between a non-whitespace character at the beginning and end of a string.
It also matches only a single non-whitespace character (unlike many of the answers here).
It will not match any newline (\n), \r, \t, \f, nor \v in the string (unlike Aprillion's answer). I realize this isn't explicit to the question, but it's a useful distinction.
Letters and numbers divided only by one space. Also, no spaces allowed at beginning and end.
/^[a-z0-9]+( [a-z0-9]+)*$/gi
I found a reliable way to do this is just to specify what you do want to allow for the first character and check the other characters as normal e.g. in JavaScript:
RegExp("^[a-zA-Z][a-zA-Z- ]*$")
So that expression accepts only a single letter at the start, and then any number of letters, hyphens or spaces thereafter.
use /^[^\s].([A-Za-z]+\s)*[A-Za-z]+$/. this one. it only accept one space between words and no more space at beginning and end
If we do not have to make a specific class of valid character set (Going to accept any language character), and we just going to prevent spaces from Start & End, The must simple can be this pattern:
/^(?! ).*[^ ]$/
Try on HTML Input:
input:invalid {box-shadow:0 0 0 4px red}
/* Note: ^ and $ removed from pattern. Because HTML Input already use the pattern from First to End by itself. */
<input pattern="(?! ).*[^ ]">
Explaination
^ Start of
(?!...) (Negative lookahead) Not equal to ... > for next set
Just Space / \s (Space & Tabs & Next line chars)
(?! ) Do not accept any space in first of next set (.*)
. Any character (Execpt \n\r linebreaks)
* Zero or more (Length of the set)
[^ ] Set/Class of Any character expect space
$ End of
Try it live: https://regexr.com/6e1o4
^[^0-9 ]{1}([a-zA-Z]+\s{1})+[a-zA-Z]+$
-for No more than one whitespaces in between , No spaces in first and last.
^[^0-9 ]{1}([a-zA-Z ])+[a-zA-Z]+$
-for more than one whitespaces in between , No spaces in first and last.
Other answers introduce a limit on the length of the match. This can be avoided using Negative lookaheads and lookbehinds:
^(?!\s)([a-zA-Z0-9\s])*?(?<!\s)$
This starts by checking that the first character is not whitespace ^(?!\s). It then captures the characters you want a-zA-Z0-9\s non greedily (*?), and ends by checking that the character before $ (end of string/line) is not \s.
Check that lookaheads/lookbehinds are supported in your platform/browser.
Here you go,
\b^[^\s][a-zA-Z0-9]*\s+[a-zA-Z0-9]*\b
\b refers to word boundary
\s+ means allowing white-space one or more at the middle.
(^(\s)+|(\s)+$)
This expression will match the first and last spaces of the article..
I'm developing a laravel application where a user can refer to his profile by putting his username in the appropriate form.
Let's see an example:
A user named John can refer to his profile using the following text: #John
I spent several hours trying to understand how regex works, but this pattern is where i've got so far: #([A-Za-z0-9]+)
This pattern perfectly matches the example above, but it also matches other formats that it normally shouldn't.
I need some help creating the perfect pattern.
It should only match a string that starts with the # symbol.
For example: #John, #Sam, #Bill, etc.
It shouldn't match a string that doesn't start with the # symbol.
For example: a#John, something#Sam, 123#Bill, etc.
It should also match those formats that contain more than one # symbols.
For example: #John#, #Sam#something, #Bill##sometext, etc.
In this case the pattern should capture: John#, Sam#something, Bill##sometext
Thanks for your help and sorry for my bad english.
This should work:
(?<=\s|^)#([\w#]+)
There is a positive lookbehind assertion to make sure the tag is preceded by whitespace, or the start of the string. After that it's just a case of consuming the # character and putting the username inside a capturing group.
Regex demo
Your regex is almost correct.
Firstly, you want to say that your regex should match also the begining of the string. You can achieve that with caret symbol (^):
^#([A-Za-z0-9]+)
Secondly, you want to be able to put the # sign inside. Now it's easy - just add that symbol inside the brackets.
^#([A-Za-z0-9#]+)
Try /(?:\s#{1,3})([a-zA-Z#.]+)/i
Explain
# Character. Matches a "#" character (char code 64).
{1,3} Quantifier. Match between 1 and 3 of the preceding token.
\w Word. Matches any word character (alphanumeric & underscore).
+ Plus. Match 1 or more of the preceding token.
Here is regexr: http://regexr.com/3djhq
I'm trying to build a regex in PHP to extract the first part of the sample strings below. The angle brackets denotes required part, square brackets denotes the optional part, and there are three possibilities of input (the brackets are not included in the input).
<Rua Olavo Bilac>
<Rua Olavo Bilac>[ - de 123...]
<Rua Olavo Bilac>[ - até ...]
(beware that the required part may have dashes)
I've tried:
/(.*?)( - (de|até){1,1}.*)?/i (the first group should capture what I needed, ungreedily)
I`ve also tried several modifications without luck. I'm probably doing some confusion here, specially with the groups and with the quantity modifiers. From what I understand:
The first group would catch any character, ungreedly
The second group, optional per the ? modifier, would have \s-\s followed by one of the two words de or até exactly one time, then any characters until the end of the line.
I ended replacing preg_match_all with strpos and substr, testing for each possibility. It did work, but I need to understand where I'm wrong about the regex approach.
You can use this regex (see demo):
^.*(?= *-(?!.*-))|^.*
How does it work?
We match two kinds of string, on either side of the |
On the left side, from the head of the string (anchored by the ^ assertion), the dot-star .* eats up any characters up to a place where the lookahead (?= *-(?!.*-)) asserts that what follows is optional space characters * and a dash -, not followed by (negative lookahead) more characters and a dash.
On the right side of the |, we match anything.
This assume that you are checking the strings line by line. If that is not the case, let us know.
Sample Code
$regex = "~^.*(?= *-(?!.*-))|^.*~";
if(preg_match($regex,$string,$m)) echo $m[0];
I'm trying to write a regular expression which will find URLs in a plain-text string, so that I can wrap them with anchor tags. I know there are expressions already available for this, but I want to create my own, mostly because I want to know how it works.
Since it's not going to break anything if my regex fails, my plan is to write something fairly simple. So far that means: 1) match "www" or "http" at the start of a word 2) keep matching until the word ends.
I can do that, AFAICT. I have this: \b(http|www).?[^\s]+
Which works on foo www.example.com bar http://www.example.com etc.
The problem is that if I give it foo www.example.com, http://www.example.com it thinks that the comma is a part of the URL.
So, if I am to use one expression to do this, I need to change "...and stop when you see whitespace" to "...and stop when you see whitespace or a piece of punctuation right before whitespace". This is what I'm not sure how to do.
At the moment, a solution I'm thinking of running with is just adding another test – matching the URL, and then on the next line moving any sneaky punctuation. This just isn't as elegant.
Note: I am writing this in PHP.
Aside: why does replacing \s with \b in the expression above not seem to work?
ETA:
Thanks everyone!
This is what I eventually ended up with, based on Explosion Pills's advice:
function add_links( $string ) {
function replace( $arr ) {
if ( strncmp( "http", $arr[1], 4) == 0 ) {
return "<a href=$arr[1]>$arr[1]</a>$arr[2]$arr[3]";
} else {
return "$arr[1]$arr[2]$arr[3]";
}
}
return preg_replace_callback( '/\b((?:http|www).+?)((?!\/)[\p{P}]+)?(\s|$)/x', replace, $string );
}
I added a callback so that all of the links would start with http://, and did some fiddling with the way it handles punctuation.
It's probably not the Best way to do things, but it works. I've learned a lot about this in the last little while, but there is still more to learn!
preg_replace('/
\b # Initial word boundary
( # Start capture
(?: # Non-capture group
http|www # http or www (alternation)
) # end group
.+? # reluctant match for at least one character until...
) # End capture
( # Start capture
[,.]+ # ...one or more of either a comma or period.
# add more punctuation as needed
)? # End optional capture
(\s|$) # Followed by either a space character or end of string
/x', '\1\2\3'
...is probably what you are going for. I think it's still imperfect, but it should at least work for your needs.
Aside: I think this is because \b matches punctuation too
You can achieve this with a positive lookahead assertion:
\b(http:|www\.)(?:[^\s,.!?]|[,.!?](?!\s))+
See it here on Regexr.
Means, match anything, but whitespace ,.!? OR match ,.!? when it is not followed by whitespace.
Aside: A word boundary is not a character or a set of characters, you can't put it into a character class. It is a zero width assertion, that is matching on a change from a word character to a non-word character. Here, I believe, \b in a character class is interpreted as the backspace character (the string escape sequence).
The problem may lie in the dot, which means "any character" in regex-speak. You'll probably have to escape it:
\b(http|www)\.?[^\s]+
Then, the question mark means 0 or 1 so you've said "an optional dot" which is not what you want (right?):
\b(http|www)\.[^\s]+
Now, it will only match http. and www. so you need to tell what other characters you'll let it accept:
\b(http|www)\.[^\s\w]+
or
\b(http|www)\.[^\sa-zA-Z]+
So now you're saying,
at the boundary of a word
check for http or www
put a dot
allow any range a-z or A-Z, don't allow any whitespace character
one or more of those
Note - I haven't tested these but they are hopefully correct-ish.
Aside (my take on it) - the \s means 'whitespace'. The \b means 'word boundary'. The [] means 'an allowed character range'. The ^ means 'not'. The + means 'one or more'.
So when you say [^\b]+ you're saying 'don't allow word boundaries in this range of characters, and there must be one or more' and since there's nothing else there > nothing else is allowed > there's not one or more > it probably breaks.
You should try something like this:
\b(http|www).?[\w\.\/]+
I have a string that looks like this:
[if-abc] 12345 [if-def] 67890 [/if][/if]
I have the following regex:
/\[if-([a-z0-9-]*)\]([^\[if]*?)\[\/if\]/s
This matches the inner brackets just like I want it to. However, when I replace the 67890 with text (ie. abcdef), it doesn't match it.
[if-abc] 12345 [if-def] abcdef [/if][/if]
I want to be able to match ANY characters, including line breaks, except for another opening bracket [if-.
This part doesn't work like you think it does:
[^\[if]
This will match a single character that is neither of [, i or f. Regardless of the combination. You can mimic the desired behavior using a negative lookahead though:
~\[if-([a-z0-9-]*)\]((?:(?!\[/?if).)*)\[/if\]~s
I've also included closing tags in the lookahead, as this avoid the ungreedy repetition (which is usually worse performance-wise). Plus, I've changed the delimiters, so that you don't have to escape the slash in the pattern.
So this is the interesting part ((?:(?!\[/?if).)*) explained:
( # capture the contents of the tag-pair
(?: # start a non-capturing group (the ?: are just a performance
# optimization). this group represents a single "allowed" character
(?! # negative lookahead - makes sure that the next character does not mark
# the start of either [if or [/if (the negative lookahead will cause
# the entire pattern to fail if its contents match)
\[/?if
# match [if or [/if
) # end of lookahead
. # consume/match any single character
)* # end of group - repeat 0 or more times
) # end of capturing group
Modifying a little results in:
/\[if-([a-z0-9-]+)\](.+?)(?=\[if)/s
Running it on [if-abc] 12345 [if-def] abcdef [/if][/if]
Results in a first match as: [if-abc] 12345
Your groups are: abc and 12345
And modifying even further:
/\[if-([a-z0-9-]+)\](.+?)(?=(?:\[\/?if))/s
matches both groups. Although the delimiter [/if] is not captured by either of these.
NOTE: Instead of matching the delimeters I used a lookahead ((?=)) in the regex to stop when the text ahead matches the lookahead.
Use a period to match any character.