web-dev-qa-db-fra.com

Remplacer un groupe de capture Regex par des majuscules en Javascript

J'aimerais savoir comment remplacer un groupe de capture par sa majuscule en JavaScript. Voici une version simplifiée de ce que j'ai essayé jusqu'à présent qui ne fonctionne pas:

> a="foobar"
'foobar'
> a.replace( /(f)/, "$1".toUpperCase() )
'foobar'
> a.replace( /(f)/, String.prototype.toUpperCase.apply("$1") )
'foobar'

Souhaitez-vous expliquer ce qui ne va pas avec ce code?

60
Evan Carroll

Vous pouvez passer une fonction à replace.

var r = a.replace(/(f)/, function(v) { return v.toUpperCase(); });

Explication

a.replace( /(f)/, "$1".toUpperCase())

Dans cet exemple, vous passez une chaîne à la fonction replace. Puisque vous utilisez la syntaxe spéciale de remplacement ($ N saisit la Nième capture), vous donnez simplement la même valeur. La toUpperCase est en réalité trompeuse parce que vous ne faites que la chaîne de remplacement majuscule (ce qui est quelque peu inutile car les caractères $ et 1 n'ont pas de majuscule et la valeur de retour sera toujours "$1").

a.replace( /(f)/, String.prototype.toUpperCase.apply("$1"))

Croyez-le ou non, la sémantique de cette expression est exactement la même.

131
ChaosPandion

Je sais que je suis en retard à la fête, mais voici une méthode plus courte qui va dans le sens de vos tentatives initiales.

a.replace('f', String.call.bind(a.toUpperCase));

Alors, où vous êtes-vous trompé et quel est ce nouveau vaudou?

Problème 1

Comme indiqué précédemment, vous tentiez de transmettre les résultats d'une méthode appelée en tant que deuxième paramètre de String.prototype.replace () , alors que vous devriez plutôt transmettre une référence à une fonction.

Solution 1

C'est assez facile à résoudre. Supprimer simplement les paramètres et les parenthèses nous donnera une référence plutôt que d’exécuter la fonction.

a.replace('f', String.prototype.toUpperCase.apply)

Problème 2

Si vous essayez d'exécuter le code maintenant, vous obtiendrez une erreur indiquant que indéfini n'est pas une fonction et ne peut donc pas être appelé. En effet, String.prototype.toUpperCase.apply est en réalité une référence à Function.prototype.apply () via l'héritage prototypique de JavaScript. Donc, ce que nous faisons en réalité ressemble plus à ceci

a.replace('f', Function.prototype.apply)

Ce qui n’est évidemment pas ce que nous avons voulu. Comment sait-il exécuter Function.prototype.apply () on String.prototype.toUpperCase () ?

Solution 2

En utilisant Function.prototype.bind () , nous pouvons créer une copie de Function.prototype.call avec son contexte spécifiquement défini sur String.prototype.toUpperCase. Nous avons maintenant le suivant

a.replace('f', Function.prototype.apply.bind(String.prototype.toUpperCase))

Problème 3

Le dernier problème est que String.prototype.replace () passera plusieurs arguments à sa fonction de remplacement. Cependant, Function.prototype.apply () s'attend à ce que le deuxième paramètre soit un tableau, mais obtient plutôt une chaîne ou un nombre (selon que vous utilisiez des groupes de capture ou non). Cela provoquerait une erreur de liste d'arguments non valide.

Solution 3

Heureusement, nous pouvons simplement substituer dans Function.prototype.call () (qui accepte un nombre quelconque d'arguments, sans restriction de type) pour Function.prototype.apply () . Nous sommes maintenant arrivés au code de travail!

a.replace(/f/, Function.prototype.call.bind(String.prototype.toUpperCase))

Suppression d'octets!

Personne ne veut taper prototype plusieurs fois. Au lieu de cela, nous exploitons le fait que nous avons des objets qui référencent les mêmes méthodes via l'héritage. Le constructeur String, en tant que fonction, hérite du prototype de Function. Cela signifie que nous pouvons remplacer par Function.prototype.call dans String.call (en fait, nous pouvons utiliser Date.call pour économiser encore plus d’octets, mais c’est moins sémantique).

Nous pouvons également utiliser notre variable 'a' car son prototype inclut une référence à String.prototype.toUpperCase nous pouvons échanger cela avec a.toUpperCase. C’est la combinaison des 3 solutions ci-dessus et de ces mesures d’économie d’octets qui nous permet d’obtenir le code en haut de ce post.

11
Joshua Piccari

Ancien post, mais il vaut la peine d'étendre la réponse de @ChaosPandion à d'autres cas d'utilisation avec un RegEx plus restreint. Par exemple. assurez-vous que le (f) ou le groupe de capture est entouré d’un format spécifique /z(f)oo/:

> a="foobazfoobar"
'foobazfoobar'
> a.replace(/z(f)oo/, function($0,$1) {return $0.replace($1, $1.toUpperCase());})
'foobazFoobar'
// Improve the RegEx so `(f)` will only get replaced when it begins with a dot or new line, etc.

Je veux simplement mettre en évidence les deux paramètres de function permet de trouver un format spécifique et de remplacer un groupe de capture dans le format possible.

5
CallMeLaNN

SOLUTION

a.replace(/(f)/,x=>x.toUpperCase())  

pour remplacer toutes les occurrences du groupe, utilisez /(f)/g regexp. Le problème dans votre code: String.prototype.toUpperCase.apply("$1") et "$1".toUpperCase() donne "$1" (essayez vous-même dans la console) - afin de ne rien changer et en fait, vous appelez deux fois a.replace( /(f)/, "$1") (qui ne change rien en plus). 

let a= "foobar";
let b= a.replace(/(f)/,x=>x.toUpperCase());
let c= a.replace(/(o)/g,x=>x.toUpperCase());

console.log("/(f)/ ", b);
console.log("/(o)/g", c);

1
Kamil Kiełczewski

Étant donné un dictionnaire (objet, dans ce cas, une Map) de propriété, de valeurs et en utilisant .bind() comme décrit dans les réponses

const regex = /([A-z0-9]+)/;
const dictionary = new Map([["hello", 123]]); 
let str = "hello";
str = str.replace(regex, dictionary.get.bind(dictionary));

console.log(str);

Utilisation d'un objet brut JavaScript et d'une fonction définie pour obtenir le retour de la valeur de propriété correspondante de l'objet ou de la chaîne d'origine si aucune correspondance n'est trouvée

const regex = /([A-z0-9]+)/;
const dictionary = {
  "hello": 123,
  [Symbol("dictionary")](prop) {
    return this[prop] || prop
  }
};
let str = "hello";
str = str.replace(regex, dictionary[Object.getOwnPropertySymbols(dictionary)[0]].bind(dictionary));

console.log(str);

0
guest271314