I'm confused by how RegEx distinguishes between apostrophes and single quotes - javascript

I’m trying to better understand RegEx and apostrophes/single quotes.
If I use this code:
const regex = /\b[\'\w]+\b/g
console.log(phrase.match(regex))
Then
let phrase = "'pickle'" // becomes pickle (single quotes disappear)
let phrase = "can't" // becomes can't (apostrophe remains)
I thought I knew what all regex do:
/text/g means everything between the slashes and g means global, to
keep searching after the first hit.
\b is word boundary, spaces on each side
w+ means alphanumerics, and the '+' indicates it can be for more
than 1 character
[\w\']+ means A-Za-z0-9 and apostrophe of any length.
But I'd like to get this:
let phrase = "'pickle'" // becomes 'pickle' (with single quotes)
What am I missing? I experimented with
const regex2 = /\b[\w+\']\b/g;
console.log(phrase.match(regex2))
let phrase = "can't"
But that becomes ["'", "t"] ... why? I understand now that the + is after the \w, the \' stands alone, but why "t" and where did the "can" go?
I tried
const regex3 = /\b\'[\w+]\'\b/g;
console.log(phrase.match(regex3))
But I get "null". Why?

The question is basically "How do I get word boundaries including apostrophes". Right?
If so, then the regex you have /\b[\'\w]+\b/g explicitly looks for \b for boundary which will match a non word character (like space or apostrohpe) followed by a letter or viceversa. Like this: https://regex101.com/r/7Pxsru/1, (I added a few more words so that the boundary is clearly seen)
If you would like to get "'pickle'" and "can't" then simply don't look for \b, like this /[\w+\']+/g, see the demo: https://regex101.com/r/FNjlEq/1
The two regexes you propose mean the following:
/\b[\w+\']\b/g: Look for a boundary letter then any word letter any number of times (note that this has no effect since it is inside a []) OR an apostrophe then a boundary.
/\b\'[\w+]\'\b/g: Look for a boundary letter by an apostrophe and any word any number of times (note that there is no need to be inside a []) then followed by an apostrophe and a word boundary.

const regex2 = /\b[\w+\']\b/g;
In this one, since the + in inside of [], it is matching a literal + character, so you're searching for a word boundary, followed by either a single alphanumeric character, a +, or a ', following by a word boundary.
You probably want:
\b(\w+|\')\b
which looks for a word boundary, followed by either at least one alphanumeric character or a single quote.
It would probably help to look at regex101 so you can see what the regex is actually doing: https://regex101.com/r/aJPWAB/1

Related

\b regex special character seems not working for Cyrillic in javascript [duplicate]

I am building search and I am going to use javascript autocomplete with it. I am from Finland (finnish language) so I have to deal with some special characters like ä, ö and å
When user types text in to the search input field I try to match the text to data.
Here is simple example that is not working correctly if user types for example "ää". Same thing with "äl"
var title = "this is simple string with finnish word tämä on ääkköstesti älkää ihmetelkö";
// Does not work
var searchterm = "äl";
// does not work
//var searchterm = "ää";
// Works
//var searchterm = "wi";
if ( new RegExp("\\b"+searchterm, "gi").test(title) ) {
$("#result").html("Match: ("+searchterm+"): "+title);
} else {
$("#result").html("nothing found with term: "+searchterm);
}
http://jsfiddle.net/7TsxB/
So how can I get those ä,ö and å characters to work with javascript regex?
I think I should use unicode codes but how should I do that? Codes for those characters are:
[\u00C4,\u00E4,\u00C5,\u00E5,\u00D6,\u00F6]
=> äÄåÅöÖ
There appears to be a problem with Regex and the word boundary \b matching the beginning of a string with a starting character out of the normal 256 byte range.
Instead of using \b, try using (?:^|\\s)
var title = "this is simple string with finnish word tämä on ääkköstesti älkää ihmetelkö";
// Does not work
var searchterm = "äl";
// does not work
//var searchterm = "ää";
// Works
//var searchterm = "wi";
if ( new RegExp("(?:^|\\s)"+searchterm, "gi").test(title) ) {
$("#result").html("Match: ("+searchterm+"): "+title);
} else {
$("#result").html("nothing found with term: "+searchterm);
}
Breakdown:
(?: parenthesis () form a capture group in Regex. Parenthesis started with a question mark and colon ?: form a non-capturing group. They just group the terms together
^ the caret symbol matches the beginning of a string
| the bar is the "or" operator.
\s matches whitespace (appears as \\s in the string because we have to escape the backslash)
) closes the group
So instead of using \b, which matches word boundaries and doesn't work for unicode characters, we use a non-capturing group which matches the beginning of a string OR whitespace.
The \b character class in JavaScript RegEx is really only useful with simple ASCII encoding. \b is a shortcut code for the boundary between \w and \W sets or \w and the beginning or end of the string. These character sets only take into account ASCII "word" characters, where \w is equal to [a-zA-Z0-9_] and \W is the negation of that class.
This makes the RegEx character classes largely useless for dealing with any real language.
\s should work for what you want to do, provided that search terms are only delimited by whitespace.
this question is old, but I think I found a better solution for boundary in regular expressions with unicode letters.
Using XRegExp library you can implement a valid \b boundary expanding this
XRegExp('(?=^|$|[^\\p{L}])')
the result is a 4000+ char long, but it seems to work quite performing.
Some explanation: (?= ) is a zero-length lookahead that looks for a begin or end boundary or a non-letter unicode character. The most important think is the lookahead, because the \b doesn't capture anything: it is simply true or false.
\b is a shortcut for the transition between a letter and a non-letter character, or vice-versa.
Updating and improving on max_masseti's answer:
With the introduction of the /u modifier for RegExs in ES2018, you can now use \p{L} to represent any unicode letter, and \P{L} (notice the uppercase P) to represent anything but.
EDIT: Previous version was incomplete.
As such:
const text = 'A Fé, o Império, e as terras viciosas';
text.split(/(?<=\p{L})(?=\P{L})|(?<=\P{L})(?=\p{L})/);
// ['A', ' Fé', ',', ' o', ' Império', ',', ' e', ' as', ' terras', ' viciosas']
We're using a lookbehind (?<=...) to find a letter and a lookahead (?=...) to find a non-letter, or vice versa.
I would recommend you to use XRegExp when you have to work with a specific set of characters from Unicode, the author of this library mapped all kind of regional sets of characters making the work with different languages easier.
Despite the fact the issue seems to be 8 years old, I run into a similar problem (I had to match Cyrillic letters) not so far ago. I spend a whole day on this and could not find any appropriate answer here on StackOverflow. So, to avoid others making lots of effort, I'd like to share my solution.
Yes, \b word boundary works only with Latin letters (Word boundary: \b):
Word boundary \b doesn’t work for non-Latin alphabets
The word boundary test \b checks that there should be \w on the one side from the position and "not \w" – on the other side.
But \w means a Latin letter a-z (or a digit or an underscore), so the test doesn’t work for other characters, e.g. Cyrillic letters or hieroglyphs.
Yes, JavaScript RegExp implementation hardly supports UTF-8 encoding.
So, I tried implementing own word boundary feature with the support of non-Latin characters. To make word boundary work just with Cyrillic characters I created such regular expression:
new RegExp(`(?<![\u0400-\u04ff])${cyrillicSearchValue}(?![\u0400-\u04ff])`,'gi')
Where \u0400-\u04ff is a range of Cyrillic characters provided in the table of codes. It is not an ideal solution, however, it works properly in most cases.
To make it work in your case, you just have to pick up an appropriate range of codes from the list of Unicode characters.
To try out my example run the code snippet below.
function getMatchExpression(cyrillicSearchValue) {
return new RegExp(
`(?<![\u0400-\u04ff])${cyrillicSearchValue}(?![\u0400-\u04ff])`,
'gi',
);
}
const sentence = 'Будь-який текст кирилицею, де необхідно знайти слово з контексту';
console.log(sentence.match(getMatchExpression('текст')));
// expected output: ["текст"]
console.log(sentence.match(getMatchExpression('но')));
// expected output: null
I noticed something really weird with \b when using Unicode:
/\bo/.test("pop"); // false (obviously)
/\bä/.test("päp"); // true (what..?)
/\Bo/.test("pop"); // true
/\Bä/.test("päp"); // false (what..?)
It appears that meaning of \b and \B are reversed, but only when used with non-ASCII Unicode? There might be something deeper going on here, but I'm not sure what it is.
In any case, it seems that the word boundary is the issue, not the Unicode characters themselves. Perhaps you should just replace \b with (^|[\s\\/-_&]), as that seems to work correctly. (Make your list of symbols more comprehensive than mine, though.)
My idea is to search with codes representing the Finnish letters
new RegExp("\\b"+asciiOnly(searchterm), "gi").test(asciiOnly(title))
My original idea was to use plain encodeURI but the % sign seemed to interfere with the regexp.
http://jsfiddle.net/7TsxB/5/
I wrote a crude function using encodeURI to encode every character with code over 128 but removing its % and adding 'QQ' in the beginning. It is not the best marker but I couldn't get non alphanumeric to work.
What you are looking for is the Unicode word boundaries standard:
http://unicode.org/reports/tr29/tr29-9.html#Word_Boundaries
There is a JavaScript implementation here (unciodejs.wordbreak.js)
https://github.com/wikimedia/unicodejs
I had a similar problem, where I was trying to replace all of a particular unicode word with a different unicode word, and I cannot use lookbehind because it's not supported in the JS engine this code will be used in. I ultimately resolved it like this:
const needle = "КАРТОПЛЯ";
const replace = "БАРАБОЛЯ";
const regex = new RegExp(
String.raw`(^|[^\n\p{L}])`
+ needle
+ String.raw`(?=$|\P{L})`,
"gimu",
);
const result = (
'КАРТОПЛЯ сдффКАРТОПЛЯдадф КАРТОПЛЯ КАРТОПЛЯ КАРТОПЛЯ??? !!!КАРТОПЛЯ ;!;!КАРТОПЛЯ/#?#?'
+ '\n\nКАРТОПЛЯ КАРТОПЛЯ - - -КАРТОПЛЯ--'
)
.replace(regex, function (match, ...args) {
return args[0] + replace;
});
console.log(result)
output:
БАРАБОЛЯ сдффКАРТОПЛЯдадф БАРАБОЛЯ БАРАБОЛЯ БАРАБОЛЯ??? !!!БАРАБОЛЯ ;!;!БАРАБОЛЯ/#?#?
БАРАБОЛЯ БАРАБОЛЯ - - -БАРАБОЛЯ--
Breaking it apart
The first regex: (^|[^\n\p{L}])
^| = Start of the line or
[^\n\p{L}] = Any character which is not a letter or a newline
The second regex: (?=$|\P{L})
?= = Lookahead
$| = End of the line or
\P{L} = Any character which is not a letter
The first regex captures the group and is then used via args[0] to put it back into the string during replacement, thereby avoiding a lookbehind. The second regex utilized lookahead.
Note that the second one MUST be a lookahead because if we capture it then overlapping regex matches will not trigger (e.g. КАРТОПЛЯ КАРТОПЛЯ КАРТОПЛЯ would only match on the 1st and 3rd ones).
Trying to find text "myTest":
/(?<![\p{L}\p{N}_])myTest(?![\p{L}\p{N}_])/gu
Similar to NetBeans or Notepad++ form. Trying to find the expression without any letter or number or underscore (like \w characters of word boundary \b) in any unicode characters of letter and number before or after the expression.
I have had a similar problem, but I had to replace an array of terms. All solutions, which I have found did not worked, if two terms were in the text next to each other (because their boundaries overlaped). So I had to use a little modified approach:
var text = "Ještě. že; \"už\" à. Fürs, 'anlässlich' že že že.";
var terms = ["à","anlässlich","Fürs","už","Ještě", "že"];
var replaced = [];
var order = 0;
for (i = 0; i < terms.length; i++) {
terms[i] = "(^\|[ \n\r\t.,;'\"\+!?-])(" + terms[i] + ")([ \n\r\t.,;'\"\+!?-]+\|$)";
}
var re = new RegExp(terms.join("|"), "");
while (true) {
var replacedString = "";
text = text.replace(re, function replacer(match){
var beginning = match.match("^[ \n\r\t.,;'\"\+!?-]+");
if (beginning == null) beginning = "";
var ending = match.match("[ \n\r\t.,;'\"\+!?-]+$");
if (ending == null) ending = "";
replacedString = match.replace(beginning,"");
replacedString = replacedString.replace(ending,"");
replaced.push(replacedString);
return beginning+"{{"+order+"}}"+ending;
});
if (replacedString == "") break;
order += 1;
}
See the code in a fiddle: http://jsfiddle.net/antoninslejska/bvbLpdos/1/
The regular expression is inspired by: http://breakthebit.org/post/3446894238/word-boundaries-in-javascripts-regular
I can't say, that I find the solution elegant...
The correct answer to the question is given by andrefs.
I will only rewrite it more clearly, after putting all required things together.
For ASCII text, you can use \b for matching a word boundary both at the start and the end of a pattern. When using Unicode text, you need to use 2 different patterns for doing the same:
Use (?<=^|\P{L}) for matching the start or a word boundary before the main pattern.
Use (?=\P{L}|$) for matching the end or a word boundary after the main pattern.
Additionally, use (?i) in the beginning of everything, to make all those matchings case-insensitive.
So the resulting answer is: (?i)(?<=^|\P{L})xxx(?=\P{L}|$), where xxx is your main pattern. This would be the equivalent of (?i)\bxxx\b for ASCII text.
For your code to work, you now need to do the following:
Assign to your variable "searchterm", the pattern or words you want to find.
Escape the variable's contents. For example, replace '\' with '\\' and also do the same for any reserved special character of regex, like '\^', '\$', '\/', etc. Check here for a question on how to do this.
Insert the variable's contents to the pattern above, in the place of "xxx", by simply using the string.replace() method.
bad but working:
var text = " аб аб АБ абвг ";
var ttt = "(аб)"
var p = "(^|$|[^A-Za-zА-Я-а-я0-9()])"; // add other word boundary symbols here
var exp = new RegExp(p+ttt+p,"gi");
text = text.replace(exp, "$1($2)$3").replace(exp, "$1($2)$3");
const t1 = performance.now();
console.log(text);
result (without qutes):
" (аб) (аб) (АБ) абвг "
I struggled hard on this. Working with French accented characters, and I managed to find this solution :
const myString = "MyString";
const regex = new RegExp(
"(?:[^À-ú]|^)\\b(" + myString + ")\\b(?:[^À-ú]|$)",
"ig"
);
What id does :
It keeps checking word-boundaries with \b before and after "MyString".
In addition to that, (?:[^À-ú]|^) and (?:[^À-ú]|$) will check if MyString is not surrounded by any accented characters
It will not work with cyrillic but it may be possible to find the range of cirillic charactes and edit [^À-ú] in consequence.
Warning, it captures only the group (MyString) but the total match contains previous and next characters
See example : https://regex101.com/r/5P0ZIe/1
Match examples :
MyString
match : "MyString"
group 1 : "MyString"
Lorem ipsum. MyString dolor sit amet
match : " MyString "
group 1 : "MyString"
(MyString)
match : "(MyString)"
group 1 : "MyString"
BetweenCharactersMyStringIsNotFound
match : Nothing
group 1 : Nothing
éMyStringé
match : Nothing
group 1 : Nothing
ùMyString
match : Nothing
group 1 : Nothing
MyStringÖ
match : Nothing
group 1 : Nothing

Split string on spaces except for in quotes, but include incomplete quotes

I am trying to split a string in JS on spaces except when the space is in a quote. However, an incomplete quote should be maintained. I'm not skilled in regex wizardry, and have been using the below regex:
var list = text.match(/[^\s"]+|"([^"]*)"/g)
However, if I provide input like sdfj "sdfjjk this will become ["sdfj","sdfjjk"] rather than ["sdfj",""sdfjjk"].
You can use
var re = /"([^"]*)"|\S+/g;
By using \S (=[^\s]) we just drop the " from the negated character class.
By placing the "([^"]*)" pattern before \S+, we make sure substrings in quotes are not torn if they come before. This should work if the string contains well-paired quoted substrings and the last is unpaired.
Demo:
var re = /"([^"]*)"|\S+/g;
var str = 'sdfj "sdfjjk';
document.body.innerHTML = JSON.stringify(str.match(re));
Note that to get the captured texts in-between quotes, you will need to use RegExp#exec in a loop (as String#match "drops" submatches).
UPDATE
No idea what downvoter thought when downvoting, but let me guess. The quotes are usually used around word characters. If there is a "wild" quote, it is still a quote right before/after a word.
So, we can utilize word boundaries like this:
"\b[^"]*\b"|\S+
See regex demo.
Here, "\b[^"]*\b" matches a " that is followed by a word character, then matches zero or more characters other than " and then is followed with a " that is preceded with a word character.
Moving further in this direction, we can make it as far as:
\B"\b[^"\n]*\b"\B|\S+
With \B" we require that " should be preceded with a non-word character, and "\B should be followed with a non-word character.
See another regex demo
A lot depends on what specific issue you have with your specific input!
Try the following:
text.match(/".*?"|[^\s]+/g).map(s => s.replace(/^"(.*)"$/, "$1"))
This repeatedly finds either properly quoted substrings (first), OR other sequences of non-whitespace. The map part is to remove the quotes around the quoted substrings.
> text = 'abc "def ghi" lmn "opq'
< ["abc", "def ghi", "lmn", ""opq"]

Find full word by part of it using regex

I have a part of the word and I should find full word in the string using regular expressions.
For example, I have the following text:
If it bothers you, call it a "const identifier" instead.
It doesn't matter whether you call max a const variable or a const identififfiieer. What matters...
And the part of the word: identifi. I have to find both: identifier and identififfiieer.
I tried the following regex (javascript):
[\ ,!##$%^&*()\.\"]*(identifi.*?)[\ ,!##$%^&*()\d\.\"]
So it searches the part of word surrounded by punctuation characters or space. Sometime this regex works fine, but in this case it also includes quote and dot int the match. What's wrong with it? Maybe there is a better idea?
You can use
\bidentifi.*?\b
Which means:
Assert the position at a word boundary
Match the characters "identifi" literally
Match any single character that is not not a line break
Between zero and unlimited times, as few times as possible, expanding as needed (lazy)
Assert the position at a word boundary
'foo "bar identifier"'.match(/\bidentifi.*?\b/g); // ["identifier"]
'foo identififfiieer. bar'.match(/\bidentifi.*?\b/g); // ["identififfiieer"]
You can use \w*identifi\w*
\w stands for "word character". It always matches the ASCII characters [A-Za-z0-9_]. Notice the inclusion of the underscore and digits.
Here is a demo, showing the regex and its matches.
As a side note, your original regex actually works fine if you use the capturing group:
var body = 'If it bothers you, call it a "const identifier" instead.\nIt doesn\'t matter whether you call max a const variable or a const identififfiieer. What matters...';
var reg = /[\ ,!##$%^&*()\.\"]*(identifi.*?)[\ ,!##$%^&*()\d\.\"]/g;
var match;
while (match = reg.exec(body)) {
console.log('>' + match[1] + '<');
}
This outputs:
>identifier<
>identififfiieer<
Here's a demo for this code.

JS & Regex: how to replace punctuation pattern properly?

Given an input text such where all spaces are replaced by n _ :
Hello_world_?. Hello_other_sentenc3___. World___________.
I want to keep the _ between words, but I want to stick each punctuation back to the last word of a sentence without any space between last word and punctuation. I want to use the the punctuation as pivot of my regex.
I wrote the following JS-Regex:
str = str.replace(/(_| )*([:punct:])*( |_)/g, "$2$3");
This fails, since it returns :
Hello_world_?. Hello_other_sentenc3_. World_._
Why it doesn't works ? How to delete all "_" between the last word and the punctuation ?
http://jsfiddle.net/9c4z5/
Try the following regex, which makes use of a positive lookahead:
str = str.replace(/_+(?=\.)/g, "");
It replaces all underscores which are immediately followed by a punctuation character with the empty string, thus removing them.
If you want to match other punctuation characters than just the period, replace the \. part with an appropriate character class.
JavaScript doesn't have :punct: in its regex implementation. I believe you'd have to list out the punctuation characters you care about, perhaps something like this:
str = str.replace(/(_| )+([.,?])/g, "$2");
That is, replace any group of _ or space that is immediately followed by punctation with just the punctuation.
Demo: http://jsfiddle.net/9c4z5/2/

simple regex to matching multiple word with spaces/multiple space or no spaces

I am trying to match all words with single or multiple spaces. my expression
(\w+\s*)* is not working
edit 1:
Let say i have a sentence in this form
[[do "hi i am bob"]]
[[do "hi i am Bob"]]
now I have to replace this with
cool("hi i am bob") or
cool("hi i am Bob")
I do not care about replacing multiple spaces with single .
I can achieve this for a single word like
\[\[do\"(\w+)\"\]\] and replacing regex cool\(\"$1\") but this does not look like an effective solution and does not match multiple words ....
I apologies for incomplete question
any help will be aprecciated
Find this Regular Expression:
/\[\[do\s+("[\w\s]+")\s*\]\]/
And do the following replacement:
'cool($1)'
The only special thing that's being done here is using character classes to our advantage with
[\w\s]+
Matches one or more word or space characters (a-z, A-Z, 0-9, _, and whitespace). That';; eat up your internal stuff no problem.
'[[do "hi i am Bob"]]'.replace(/\[\[do\s+("[\w\s]+")\s*\]\]/, 'cool($1)')
Spits out
cool("hi i am Bob")
Though - if you want to add punctuation (which you probably will), you should do it like this:
/\[\[do\s+("[^"]+")\s*\]\]/
Which will match any character that's not a double quote, preserving your substring. There are more complicated ones to allow you to deal with escaped quotation marks, but I think that's outside the scope of this question.
To match "all words with single or multiple spaces", you cannot use \s*, as it will match even no spaces.
On the other hand, it looks like you want to match even "hi", which is one word with no spaces.
You probably want to match one or more words separated by spaces. If so, use regex pattern
(\w+(?:$|\s+))+
or
\w+(\s+\w+)*
I'm not sure, but maybe this is what you're trying to get:
"Hi I am bob".match(/\b\w+\b/g); // ["Hi", "I", "am", "bob"]
Use regex pattern \w+(\s+\w+)* as follows:
m = s.match(/\w+(\s+\w+)*/g);
Simple. Match all groups of characters that are not white spaces
var str = "Hi I am Bob";
var matches = str.match(/[^ ]+/g); // => ["Hi", "I", "am", "Bob"]
What your regex is doing is:
/([a-zA-Z0-9_]{1,}[ \r\v\n\t\f]{0,}){0,}/
That is, find the first match of one or more of A through Z bother lower and upper along with digits and underscore, then followed by zero or more space characters which are:
A space character
A carriage return character
A vertical tab character
A new line character
A tab character
A form feed character
Then followed by zero or more of A through Z bother lower and upper along with digits and underscore.
\s matches more than just simple spaces, you can put in a literal space, and it will work.
I believe you want:
/(\w+ +\w+)/g
Which all matches of one or more of A through Z bother lower and upper along with digits and underscore, followed by one or more spaces, then followed by one or more of A through Z bother lower and upper along with digits and underscore.
This will match all word-characters separated by spaces.
If you just want to find all clusters of word characters, without punctuation or spaces, then, you would use:
/(\w+)/g
Which will find all word-characters that are grouped together.
var regex=/\w+\s+/g;
Live demo: http://jsfiddle.net/GngWn/
[Update] I was just answering the question, but based on the comments this is more likely what you're looking for:
var regex=/\b\w+\b/g;
\b are word boundaries.
Demo: http://jsfiddle.net/GngWn/2/
[Update2] Your edit makes it a completely different question:
string.replace(/\[\[do "([\s\S]+)"\]\]/,'cool("$1")');
Demo: http://jsfiddle.net/GngWn/3/

Categories

Resources