How to select/find opening and closing quotes in a string using regex?

Asked

Viewed 113 times

0

Passed a string I must apply a regex that selects the opening and closing quotes, ignoring any occurrence of quotation marks in the internal scope of the string. See some examples:

"Deve selecionar as aspas de abertura e fechamento"
'Deve selecionar as aspas de abertura e fechamento'
"Deve ignorar essa ocorrência interna 'de aspas simples' e selecionar somente as de abertura e fechamento da string"
"Deve ignorar essa ocorrência escapada \"de aspas duplas\" e selecionar somente as de abertura e fechamento da string"
Essa string não tem aspas de abertura e fechamento, então deve ser ignorada
Essa string tem aspas aqui', porém, como não é no inicio e no fim, deve ser ignorada

This approach will allow me to apply some match validation as well as allow to remove these quotes since this is one of my goals.

So, briefly, how can I use regex to select the quotes that involve a string, whether single or double?

There is another rule that is: if the string starts and ends with a different quote pattern the match should not be true/occur. Ex:

'Não deve selecionar as aspas pois abertura e encerramento são diferentes"

The only result I could get was (?:^["']|["']$), however, does not answer as described.

  • if the string always has that format, you can remove the first and last characters from the string, using substring and you don’t even need a regex

  • I supplemented the post to be more complete. Not all strings have opening and closing quotes.

  • How is this string passed to the program? Is each line an element in an array or is everything just text? I ask because depending on the structure the answer is different.

  • It is passed as a single string. The function that does this parse/treat receives a literal string every time it is called.

1 answer

2


Getting the contents in quotes

If you have exactly one sentence per line, you don’t need regex. Just use split to separate lines and then see if each line starts and ends with quotes:

const texto = `"Deve selecionar as aspas de abertura e fechamento"
'Deve selecionar as aspas de abertura e fechamento'
"Deve ignorar essa ocorrência interna 'de aspas simples' e selecionar somente as de abertura e fechamento da string"
"Deve ignorar essa ocorrência escapada \\"de aspas duplas\\" e selecionar somente as de abertura e fechamento da string"
"Aspas de abertura diferente do fechamento'
Essa string não tem aspas de abertura e fechamento, então deve ser ignorada
Essa string tem aspas aqui ', porém, como não é no inicio e no fim, deve ser ignorada`;

for (const linha of texto.split('\n')) {
    let primeiro = linha[0]; // primeiro caractere da linha
    let ultimo = linha.slice(-1); // último caractere da linha
    // se começa com aspas e termina com a mesma aspas
    if ((primeiro === '"' || primeiro === "'") && primeiro === ultimo) {
        // pega o texto entre as aspas
        console.log(linha.slice(1, -1));
    }
}

If the string has Windows line breaks (\r\n), you can switch to texto.split(/[\r\n]+/) (so, he considers one or more \r or \n in sequence).


Of course it can also be done with regex. Yours didn’t work because it only checks if it starts with quotes or ends with quotation marks (only one of these conditions is sufficient). Also, it does not guarantee that the opening quotes are the same as the closure (it could start with " and end with ' or vice versa).

With regex would look like this:

const texto = `"Deve selecionar as aspas de abertura e fechamento"
'Deve selecionar as aspas de abertura e fechamento'
"Deve ignorar essa ocorrência interna 'de aspas simples' e selecionar somente as de abertura e fechamento da string"
"Deve ignorar essa ocorrência escapada \\"de aspas duplas\\" e selecionar somente as de abertura e fechamento da string"
"Aspas de abertura diferente do fechamento'
Essa string não tem aspas de abertura e fechamento, então deve ser ignorada
Essa string tem aspas aqui ', porém, como não é no inicio e no fim, deve ser ignorada`;

let r = /^(["'])(.+)\1$/gm;
for (const match of texto.matchAll(r)) {
    console.log(match[2]); // pega somente o texto entre as aspas
}

First I put the opening quotes in parentheses, forming a catch group (and since it is the first pair of parentheses of the expression, then it is the first group). At the end I use the back-Ference \1, which means "the same that was captured in group 1", so I guarantee that the opening and closing quotes should be the same.

I also use the markers ^ and $, which indicate the beginning and end of the string, but thanks to flag m, they also indicate the beginning and end of a line. So I guarantee that the quotes should be at the beginning and end of the line.

In between I put .+, indicating "one or more characters", and within parentheses to form another capture group (in this case, group 2).

I also use the flag g to take all occurrences and within the for i print the contents of group 2 (which is the text in the quotes). If you want all the text, including the quotes, use match[0].


matchAll may not work in older browsers, so an alternative is to use exec:

const texto = `"Deve selecionar as aspas de abertura e fechamento"
'Deve selecionar as aspas de abertura e fechamento'
"Deve ignorar essa ocorrência interna 'de aspas simples' e selecionar somente as de abertura e fechamento da string"
"Deve ignorar essa ocorrência escapada \\"de aspas duplas\\" e selecionar somente as de abertura e fechamento da string"
"Aspas de abertura diferente do fechamento'
Essa string não tem aspas de abertura e fechamento, então deve ser ignorada
Essa string tem aspas aqui ', porém, como não é no inicio e no fim, deve ser ignorada`;

let r = /^(["'])(.+)\1$/gm;
let match;
while (match = r.exec(texto)) {
    console.log(match[2]); // pega somente o texto entre as aspas
}


But if nay for one sentence per line, then it gets a little more complicated:

const texto = `"Deve selecionar as aspas de abertura e fechamento" blablabla 'Deve selecionar as aspas de abertura e fechamento' lorem ipsum "Deve ignorar essa ocorrência interna 'de aspas simples' e selecionar somente as de abertura e fechamento da string" fdsafadsf asd "Deve ignorar essa ocorrência escapada \\"de aspas duplas\\" e selecionar somente as de abertura e fechamento da string" fdasfasdfdsa Essa string não tem aspas de abertura e fechamento, então deve ser ignorada
Essa string tem aspas aqui ', porém, como não é no inicio e no fim, deve ser ignorada`;

let r = /"((?:[^"\\]|\\.)+)"|'((?:[^'\\]|\\.)+)'/g;
for (const match of texto.matchAll(r)) {
    if (match[0][0] === '"') {
        console.log(match[1]); // pega somente o texto entre as aspas
    } else {
        console.log(match[2]);
    }
}

The idea is to pick up the quotes, and in the middle I can have [^"\\]|\\.:

  • [^"\\]: a character that is neither quotation marks nor \, or
  • \\.: one \ followed by any character (so he picks up the escapes)

I do the same for single quotes. The contents between the quotes will be in group 1 or 2, depending on which quotes are taken.

This case only does not detect when it has different opening and closing quotes in the middle of the text (for example, bla "abc' xyz), because then it becomes impossible to detect whether the different quotation marks are part of the same sentence or not, and will have many false positives (one of them can be considered the opening or closing of other quotation marks that appear before or after, and there would have to be some other criterion to separate the sentences).



Removing the quotation marks

Now if the idea is to remove the quotes, just make a few modifications.

With split:

const texto = `"Deve selecionar as aspas de abertura e fechamento"
'Deve selecionar as aspas de abertura e fechamento'
"Deve ignorar essa ocorrência interna 'de aspas simples' e selecionar somente as de abertura e fechamento da string"
"Deve ignorar essa ocorrência escapada \\"de aspas duplas\\" e selecionar somente as de abertura e fechamento da string"
"Aspas de abertura diferente do fechamento'
Essa string não tem aspas de abertura e fechamento, então deve ser ignorada
Essa string tem aspas aqui ', porém, como não é no inicio e no fim, deve ser ignorada`;

let novoTexto = '';
for (const linha of texto.split(/(\n)/)) {
    let primeiro = linha[0]; // primeiro caractere da linha
    let ultimo = linha.slice(-1); // último caractere da linha
    // se começa com aspas e termina com a mesma aspas
    if ((primeiro === '"' || primeiro === "'") && primeiro === ultimo) {
        // pega o texto entre as aspas
        novoTexto += linha.slice(1, -1);
    } else novoTexto += linha;
}
console.log(novoTexto);

I do the split using (\n) (because when there is a capture group in regex, line breaks are included in the result). So I can concatenate the line breaks of the original string.


And with regex:

const texto = `"Deve selecionar as aspas de abertura e fechamento"
'Deve selecionar as aspas de abertura e fechamento'
"Deve ignorar essa ocorrência interna 'de aspas simples' e selecionar somente as de abertura e fechamento da string"
"Deve ignorar essa ocorrência escapada \\"de aspas duplas\\" e selecionar somente as de abertura e fechamento da string"
"Aspas de abertura diferente do fechamento'
Essa string não tem aspas de abertura e fechamento, então deve ser ignorada
Essa string tem aspas aqui ', porém, como não é no inicio e no fim, deve ser ignorada`;

let novoTexto = texto.replace(/^(["'])(.+)\1$/gm, '$2');
console.log(novoTexto);

In the replace i use $2, which corresponds to the second catch group (which in this case is the content between the quotation marks).

In case you don’t have a sentence on each line:

const texto = `"Deve selecionar as aspas de abertura e fechamento" blablabla 'Deve selecionar as aspas de abertura e fechamento' lorem ipsum "Deve ignorar essa ocorrência interna 'de aspas simples' e selecionar somente as de abertura e fechamento da string" fdsafadsf asd "Deve ignorar essa ocorrência escapada \\"de aspas duplas\\" e selecionar somente as de abertura e fechamento da string" fdasfasdfdsa Essa string não tem aspas de abertura e fechamento, então deve ser ignorada
Essa string tem aspas aqui ', porém, como não é no inicio e no fim, deve ser ignorada`;

let novoTexto = texto.replace(/"((?:[^"\\]|\\.)+)"|'((?:[^'\\]|\\.)+)'/g, '$1$2');
console.log(novoTexto);

Since the contents in quotes can be in group 1 or 2, I use $1$2 (the group that does not occur will be empty, so it works).

  • I can use in an replace in my typescript code to remove only the quotes that match this regex?

  • @Fábiojânio I updated the answer

Browser other questions tagged

You are not signed in. Login or sign up in order to post.