En js, une expression régulière est représentée par un objet RegExp. RegExp est l'abréviation d'expression régulière. Le modèle simple RegExp peut être un seul caractère. Les modèles plus complexes incluent plus de caractères et peuvent être utilisés pour l'analyse, la vérification du format, la substitution, etc. Vous pouvez utiliser un constructeur RegExp() pour créer un objet RegExp ou utiliser une syntaxe littérale.
1. Introduction à RegExp.
RegExp est une expression régulière (expression régulière, souvent abrégée en regex, regexp ou RE/re/reg dans le code), qui utilise une seule chaîne pour décrire et faire correspondre une série de modèles de recherche de chaîne conformes à une certaine règle de syntaxe. Le mode de recherche est disponible pour la recherche de texte et le remplacement de texte.
L'expression régulière est un modèle de recherche formé par une séquence de caractères. Lorsque vous recherchez des données dans du texte, vous pouvez utiliser le modèle de recherche pour décrire le contenu que vous souhaitez interroger. qui décrit le modèle de caractères, qui peut être utilisé pour faire correspondre des modèles de chaînes et rechercher et remplacer. Il s'agit d'un outil puissant pour effectuer une correspondance de modèles sur des chaînes.
La soi-disant expression régulière peut être directement comprise comme une règle ou une expression de modèle, qui exprime une certaine règle qu'un ordinateur peut comprendre, mais est une expression textuelle difficile à comprendre pour les gens ordinaires. toutes les recherches de texte et remplacements de texte, en termes simples, consistent à traiter des chaînes.
2. Méthode de chaîne.
(1), charAt() Récupère un certain caractère et renvoie un certain caractère dans la chaîne.
(2), split() Divisez la chaîne et obtenez un tableau.
(3), search()Trouvez la position où un certain caractère apparaît pour la première fois. Il est préférable de l'utiliser avec des expressions régulières. La valeur de retour est une valeur numérique. Si elle n'est pas trouvée, -1 est renvoyée.
(4), match()Trouvez le caractère spécifié dans la chaîne et renvoyez le caractère. Si vous n'utilisez pas d'expressions régulières, seule la première occurrence du caractère spécifié sera renvoyée et aucune correspondance descendante ne sera effectuée. Si vous utilisez des expressions régulières et effectuez une correspondance globale, renvoie tous les caractères spécifiés dans la chaîne sous forme de tableau ou renvoie null s'il n'est pas trouvé.
(5), replace() Remplace les caractères et renvoie une nouvelle chaîne. Il est mieux utilisé avec les expressions régulières et peut remplacer toutes les correspondances.
<script> var str='abcdefgca'; //返回字符串中某一位的字符。 alert(str.charAt()); //返回:d //查找字符串首次出现的位置。 alert(str.search('z')); //返回:- //查找指定的字符。 //只返回第一次出现的c,不再向后匹配。 alert(str.match('c')); //返回:c //将'a'替换为'i'。 //只替换了第一个位置的a,不再向后匹配。 alert(str.replace('a', 'i')); //返回:ibcdefgca //分割字符串。 var str='--aaa--cd'; var arr=str.split('-'); //返回:,,aaa,,cd alert(arr); </script>
Exemple : utilisez des méthodes ordinaires pour rechercher tous les nombres dans une chaîne
Idée d'implémentation : il n'est pas difficile de trouver les nombres dans la chaîne. Faites d'abord appel au jugement pour extraire les nombres dans la chaîne. Ensuite, il doit y avoir plus d'un nombre dans la chaîne, donc une chaîne vide est nécessaire pour la stocker. . Extrayez les caractères numériques, puis ajoutez ces caractères numériques au tableau, et enfin revenez, et vous avez terminé. Voyons comment le programme est mis en œuvre :
<script> var str=' abc d aa c zz -=-=s-'; var arr=[]; var num=''; //首先循环遍历字符串 for(var i=;i<str.length;i++){ //再判断当前字符大于等于并且小于等于,则为数字 if(str.charAt(i)>='' && str.charAt(i)<=''){ //那么就将当前的字符存储在空字符串中 num += str.charAt(i); } else{ //如果字符串中有值。 if(num){ //将值添加到数组中。 arr.push(num); //再清空字符串,避免重复添加。 num=''; } } } //最后在整个字符串结束之后有可能还会有数字,再做一次判断。 if(num){ //如果还有值就添加到数组中。 arr.push(num); //再清空字符串。 num=''; } //返回:OK,现在返回就完成了。 alert(arr); //返回:,,,,,,, </script>
Bien qu'il puisse être complété à l'aide de méthodes ordinaires et ait une structure claire, le code est relativement long si vous utilisez des expressions régulières, alors une expression peut effectuer autant de travail, ce qui est très pratique. utilisez des expressions régulières.
3. Utilisez des règles régulières.
Syntaxe de l'expression régulière : var re = new RegExp('pattern', 'modifier');
Le modèle est le modèle de l'expression, et le modificateur est utilisé pour spécifier la correspondance globale, l'insensibilité à la casse, etc. La forme complète est une expression régulière.
Étant donné que la syntaxe régulière ressemble à ceci, n'est-ce pas la syntaxe typique des objets nouvellement créés en JS. Au fait, cela signifie créer un nouvel objet régulier ? Nous savons tous que nous devrions essayer d'éviter d'utiliser le mot-clé new. L'utilisation de new créera sans aucun doute un nouvel objet, ce qui signifie également qu'il occupera une certaine quantité d'espace mémoire. S'il n'est pas géré correctement, une accumulation excessive entraînera un débordement de mémoire, ce qui est le cas. assez coûteux en ressources, ce qui n'est pas propice à la mise en œuvre de l'optimisation du code. En même temps, cette façon d'écrire ne reflète pas le pouvoir de la régularité. Elle doit donc être très concise. Par conséquent, dans la pratique, cette syntaxe régulière de style JS n'est pas utilisée, mais un autre style est utilisé :<. 🎜>
Syntaxe : var re = /mode/modifier;Ce style est relativement concis et constitue un moyen d'expression que les gens ordinaires ne peuvent pas comprendre.
(1), modificateur.
Le modificateur est utilisé pour effectuer la correspondance globale et la sensibilité à la casse.
Ignorer la casse : i (abréviation de ignorer, la traduction chinoise est : ignorer)
Correspondance globale : g (abréviation de global, traduction chinoise : all/global)
Exemple : Recherche globale des caractères spécifiés
<script> var str='AbCdEFgiX'; //JS风格: //这个正则表达式什么也不代表,只代表abc本身。 var reg=new RegExp('abc', 'i'); alert(str.match(reg)); //返回:AbC //常用风格: var re=/efg/i; alert(str.match(re)); //返回:EFg </script>
4. Crochets et métacaractères.
(1), crochets.Les crochets sont utilisés pour rechercher des caractères dans une certaine plage.
①、N'importe quel personnage
Expression : [abc]
Recherchez n’importe quel caractère entre crochets.
[] signifie ici ou, c'est-à-dire, celui qui apparaît fera l'affaire.
<script> var str='apc xpc ppc bpc spc opc'; //[apx]pc,随便出现哪个都行,即:apc ppc xpc var re=/[apx]pc/g; alert(str.match(re)); //返回前个pc。 </script>
②、范围查找。
表达式:[0-9] [a-z] [A-z] [A-Z]
[0-9] 查找任意 0 - 9 的数字。
[a-z] 查找任意 a - z 的字符。
[A-z] 查找任意 大写A - 小写z 的字符。
[A-Z] 查找任意 大写A - 大写Z的字符。
③、排除查找。
表达式:[^abc] [^a-z] [^0-9]
[^abc] 查找任意不在方括号中的字符。
[^a-z] 查找任意除了字母以外的字符,包括数字符号中文外文。
[^0-9] 查找任意除了数字以外的字符,包括字母符号中文外文。
<script> var str='ot out o.t o t o`t ot ot'; //o和t中间除了数字,什么都可以 var re=/o[^-]t/g; alert(str.match(re)); //返回:out,o.t,o t,o`t </script>
④. Sélectionnez la recherche.
Expression : (a|b|c)
Trouvez n'importe quelle option spécifiée, a ou b ou c.
⑤. La correspondance peut également utiliser le mode combinaison, tel que : [a-z0-9A-Z] [^a-z0-9]
[a-z0-9A-Z] Toutes les lettres et chiffres majuscules ou minuscules.
[^a-z0-9] Tout sauf les lettres et les chiffres est acceptable.
(2) Métacaractères.
Les métacaractères sont des caractères ayant une signification particulière et peuvent également être appelés caractères d'échappement.
Voici quelques métacaractères couramment utilisés :
Métacaractères | Description | Utiliser |
. | Trouver un seul caractère, représentant n'importe quel caractère, à l'exclusion des nouvelles lignes et des terminateurs de ligne. | Son utilisation n'est pas recommandée, elle est sujette à des problèmes. |
w | Trouver des caractères de mots, y compris des chiffres anglais et des traits de soulignement, équivalents à [a-z0-9] | /w/ |
W | Trouver des caractères autres que des mots, équivalents à [^a-z0-9] | /W/ |
d | Trouver un nombre équivalent à [0-9] | /d/ |
D | Trouver des non-chiffres, équivalents à [^0-9] | /D/ |
s | Rechercher les caractères d'espacement, y compris les espaces, les retours chariot, les tabulations, les sauts de ligne et les sauts de formulaire ne peuvent pas être affichés. | /s/ |
S | Trouver des caractères autres que des espaces. | /S/ |
b | Trouve une correspondance au début ou à la fin du mot, ou renvoie null si aucune correspondance n'est trouvée. | /b/ |
B |
Trouvez des correspondances qui ne sont pas des limites de mots, c'est-à-dire qu'elles ne se trouvent pas au début ou à la fin. Le type des caractères précédent et suivant dans la position correspondante est le même : c'est-à-dire qu'ils doivent tous deux être des mots. , ou doivent tous deux être des non-mots, au début et à la fin de la chaîne sont considérés comme des caractères non-mots, s'ils ne correspondent pas, null est renvoyé. |
/B/ |
n | Recherche un caractère de nouvelle ligne et renvoie cette position s'il est trouvé, ou -1 s'il n'est pas trouvé. | /n/ |
f | Trouver des sauts de page. | /f/ |
r | Recherchez le caractère retour chariot. | /r/ |
t | Rechercher les caractères de tabulation. |
5. Quantificateur.
Le soi-disant quantificateur est un quantificateur, c'est-à-dire un nombre. Il est utilisé dans les expressions régulières pour exprimer le nombre d'occurrences.
Voici quelques quantificateurs couramment utilisés :
Quantificateur | Description | Utiliser |
* | zéro ou n'importe quel nombre de fois, équivalent à {0,} | Déconseillé, la plage est trop large et pas assez précise. |
? | zéro ou une fois, équivalent à {0, 1} | /10?/g Effectue une recherche globale de 1, incluant zéro ou 1 '0' qui le suit immédiatement. |
une ou plusieurs fois, équivalent à {1, } | /w /g Effectuer une recherche globale d'au moins un mot. | |
{n} | Exactement n fois | /d{4}/g effectue une recherche globale de nombres à quatre chiffres. |
{n,} | Au moins n fois, sans limite au maximum | /d{3,}/g Effectue une recherche globale de nombres contenant au moins trois chiffres. |
{n, m} | Au moins n fois, au plus m fois | /d{3,4}/g Effectue une recherche globale de nombres contenant trois ou quatre chiffres. |
Voici quelques modèles de correspondance couramment utilisés :
Mode | Description | Utiliser |
^a | Tout caractère commençant par a signifie le début de la ligne | /^d/ Commence par un chiffre /^a/g Recherche globale des caractères commençant par 'a' |
un$ | Tout caractère se terminant par un, indiquant la fin de la ligne | /d$/ Finissant par un chiffre /z$/g Recherche globale des caractères finissant par 'z' |
?=a | N'importe quel caractère suivi d'un | /a(?= b)/g Recherche globale de 'a' suivi des caractères 'b' |
?!a | Tout caractère non suivi d'un | /c(?= d)/g Recherche globale des caractères qui ne suivent pas 'd' après 'c' |
6、字符串和正则配合。
(1)、search()配合正则
实例:找出字符串中第一次出现数字的位置
<script> var str='asdf zxcvbnm'; //元字符d,表示转义为数字 var re=/\d/; alert(str.search(re)); //返回: 第一个数字为出现在第位 </script>
(2)、match()配合正则
其实没有什么东西是非正则不可的,只是正则可以让做东西更方便。下面就完成本章遗留的历史问题,怎么使用正则,能一句代码就完成普通方法需要很多行代码才能完成的东西。
在实例之前,先看看match()与正则的配合。
<script> var str='asdf zxcvm'; //找出字符串中的数字可以使用元字符\d var re=/\d/; //没告诉系统要找多少数字,系统在找到数字后就返回 alert(str.match(re)); //返回: //因此需要全局匹配,使用修饰符g var re=/\d/g; //没告诉系统要找几位,系统会将所有找到的数字返回 alert(str.match(re)); //返回:,,,,,,,,,,,, //所以可以使用两个元字符,告诉系统要找的数字是位 var re=/\d\d/g; //显然这样是不可取的,因为数字的位数并不固定,可能是位,有可能还是多位 alert(str.match(re)); //返回:,,,, //所以需要用到量词+,+代表若干,也就是多少都可以。 var re=/\d+/g; //现在返回正确。 alert(str.match(re)); //返回:,,,, </script>
实例:使用正则找出字符串中所有数字
<script> var str=' abc d aa c zz -=-=s-'; //alert(str.match(/\d+/g)); //元字符d也可以使用[-]代替,到随便哪个都行。 alert(str.match(/[-]+/g)); //返回:,,,,,,, </script>
正则是强大的字符串匹配工具,就这样简单的使用一句代码就完成了。
(3)、replace()配合正则
<script> var str='abc zaaz deaxcaa'; //将字符串中的a替换为数字 alert(str.replace('a', )); //仅仅只将第一个a替换为 //配合正则使用匹配所有a再替换 var re=/a/g; alert(str.replace(re, '')); //返回所有的a都为 </script>
实例:简单的敏感词过滤
所谓的敏感词,就是法律不允许的词语,一切非法词都可以叫做敏感词,这包括的范围就太广了,比如危害国家安全,反对宪法确立的基本原则,散步谣言,扰乱民心,扰乱社会秩序,破坏社会稳定,色情、暴力、赌博、虚假、侵害、骚扰、粗俗、猥亵或其他道德上令人反感的词,以及含有法律规定或禁止的其他内容的词语。在平时最常见也是大多数人都会用的词莫属道德上令人反感的词了,说斯文一点就是吵架时用于攻击别人的词语。这里就列举几个热门的网络词语作为例子。
<!DOCTYPE html> <html> <head> <meta charset="UTF-"> <title>JavaScript实例</title> <script> window.onload=function (){ var oBtn=document.getElementById('btn'); var oTxt=document.getElementById('txt'); var oTxt=document.getElementById('txt'); oBtn.onclick=function (){ //这里的|在正则中表示 或 的意思 var re=/元芳|萌萌哒|然并卵|毛线|二货|城会玩/g; //文本框的值等于文本框的值过滤掉敏感词 oTxt.value=oTxt.value.replace(re,'***'); }; }; </script> </head> <body> <textarea id="txt" rows="" cols=""></textarea><br> <input id="btn" type="button" value="过滤"><br> <textarea id="txt" rows="" cols=""></textarea> </body> </html>
可在第一个文本框中输入一些相关语句,点击过滤按钮,查看过滤后的效果。
此外,支持正则表达式的 String 对象的方法还包括 split() 方法,可把字符串分割为字符串数组。
7、RegExp对象方法。
在JS中,RegExp对象是一个预定义了属性和方法的正则表达式对象。
(1)、test()
test() 方法用于检测一个字符串是否匹配某个模式,也就是检测指定字符串是否含有某个子串,如果字符串中含有匹配的文本,返回 true,否则返回 false。
语法:RegExpObject.test(str)
调用 RegExp 对象 re 的 test() 方法,并为它传递字符串str,与这个表示式是等价的:(re.exec(str) != null)。
实例:搜索字符串是否含有指定的字符
<script> var str='The best things in life are free, like hugs, smiles, friends, kisses, family, love and good memories.'; var re=/i/; alert(re.test(str)); //返回:true var reg=/z/; alert(reg.test(str)); //返回:false //上面的代码可以不用定义正则的变量,直接使用,将两行合并为一行。 alert(/i/.test(str)); alert(/z/.test(str)); </script>
(2)、exec()
exec() 方法用于检索字符串中的正则表达式的匹配,提取指定字符串中符合要求的子串,该方法返回一个数组,其中存放匹配的结果。如果未找到匹配,则返回 null。可以使用循环提取所有或者指定index的数据。
语法:RegExpObject.exec(str)
exec() 方法的功能非常强大,它是一个通用的方法,可以说是test() 方法的升级版,因为他不仅可以检测,而且检测到了可以直接提取结果。该方法使用起来比 test() 方法以及支持正则表达式的 String 对象的方法更为复杂。
<script> var str = 'good good study day day up'; var re = /good/; var arr = re.exec(str); console.log(arr); //控制台显示:["good"]点开后显示: "good",index ,input "good good study day day up"。 console.log(arr.index); //控制台显示: console.log(arr.input); //控制台显示:good good study day day up </script>
通过上面的实例,可以看到,如果 exec() 找到了匹配的文本,则返回一个结果数组。否则,返回 null。此数组的第 0 个元素是与正则表达式相匹配的文本,第 1 个元素是与 RegExpObject 的第 1 个子表达式相匹配的文本(如果有的话),第 2 个元素是与 RegExpObject 的第 2 个子表达式相匹配的文本(如果有的话),以此类推。
除了数组元素和 length 属性之外,exec() 方法还返回两个属性。index 属性声明的是匹配文本的第一个字符的位置。input 属性则存放的是被检索的字符串 string。我们可以看出,在调用非全局的 RegExp 对象的 exec() 方法时,返回的数组与调用方法 String.match() 返回的数组是相同的。
什么是"与子表达式相匹配的文本"?
所谓的子表达式就是正则表达式中包含在圆括号中的内容。看下面实例:
<script> var str = 'good good study day day up'; var re = /g(o+)d/; var arr = re.exec(str); console.log(arr); //显示:["good", "oo"]点开后显示: "good", "oo", index ,input: "good good study day day up" console.log(arr.length); //显示: var reg = /(o+)/; //var reg = /o+/; 只返回一个"oo",长度为 var arr = reg.exec(str); console.log(arr); //显示:["oo", "oo"]点开后显示: "oo", "oo", index ,input: "good good study day day up" console.log(arr.length); //显示: </script>
通过上例,可以看到,子表达式是一个大的表达式的一部分,并且必须用()包含起来。一个表达式可使用多个子表达式,同时还支持多层嵌套,把一个表达式划分为多个子表达式的目的是为了把那些子表达式当作一个独立的元素来使用。也就是说表达式中的子表达式可以作为整个表达式返回,也可以作为一个单独的表达式返回。所以上面的数组长度为 2。
使用子表达式是为了提取匹配的子字符串,表达式中有几个()就有几个相应的匹配字符串,顺序会依照()出现的顺序依次进行,并且()中可以使用 或"|" 进行多个选择。也就是说可以使用()对字符进行分组,并保存匹配的文本。
如果该方法使用全局匹配,则找到第一个指定字符,并存储其位置,如果再次运行 exec(),则从存储的位置(lastIndex)开始检索,并找到下一个指定字符,存储其位置。lastIndex属性是RegExp对象属性,是一个整数,标示开始下一次匹配的字符位置。看下面实例:
<script> var str = 'good good study day day up'; var re = /good/g; var arr; do{ arr = re.exec(str); console.log(arr); console.log(re.lastIndex); } while(arr !== null) /* 结果如下: 显示:["good"],点开后: "good", index , input "good good study day day up"。 lastIndex为。 显示:["good"],点开后: "good", index , input "good good study day day up"。 lastIndex为。 显示:null lastIndex为。 */ </script>
在调用非全局的 RegExp 对象的 exec() 方法时,返回的数组与调用 String.match() 返回的数组是相同的。但是,当 RegExpObject 是一个全局正则表达式时,exec() 的行为就稍微复杂一些。它会在 RegExpObject 的 lastIndex 属性指定的字符处开始检索字符串 string。当 exec() 找到了与表达式相匹配的文本时,在匹配后,它将把 RegExpObject 的 lastIndex 属性设置为匹配文本的最后一个字符的下一个位置。这就是说,可以通过反复调用 exec() 方法来遍历字符串中的所有匹配文本。当 exec() 再也找不到匹配的文本时,它将返回 null,并把 lastIndex 属性重置为 0。
通过上面实例,可以看到,当第三次循环时,找不到指定的 "good",于是返回null,lastIndex值也变成0了。找到的第一个"good"的lastIndex值为4,是匹配文本最后一个字符的下一个位置。
<script> var str = 'good good study day day up'; var re = /good/g; var arr; while((arr = re.exec(str)) != null){ console.log(arr); console.log(re.lastIndex); } /* 结果如下: 显示:["good"],点开后: "good", index , input "good good study day day up"。 lastIndex为。 显示:["good"],点开后: "good", index , input "good good study day day up"。 lastIndex为。 */ </script>
这里需要注意,如果在一个字符串中完成了一次模式匹配之后要开始检索新的字符串(仍然使用旧的re),就必须手动地把 lastIndex 属性重置为 0。
无论 RegExpObject 是否是全局模式,exec() 都会把完整的细节添加到它返回的数组中。这就是 exec() 与 String.match() 的不同之处,后者在全局模式下返回的信息要少得多。因此可以这么认为,在循环中反复地调用 exec() 方法是唯一一种获得全局模式的完整模式匹配信息的方法。
(3)、compile
compile() 方法用于在脚本执行过程中编译正则表达式,也可用于改变和重新编译正则表达式。主要作用是改变当前(re)匹配模式。
语法:RegExpObject.compile(模式, 修饰符)
模式就是正则表达式,修饰符用于规定匹配的类型,g匹配全局,i忽略大小写,gi全局匹配忽略大小写。
该方法是改变匹配模式时使用的,一般情况下,能用到的地方很少。
实例:在全局中忽略大小写 搜索"day",并用 "天" 替换,然后通过compile()方法,改变正则表达式,用 "日" 替换 "Today" 或 "day"。
<script> var str = 'Today is a beautiful day, Day day happy!'; var re = /day/gi; var str = str.replace(re, '天'); console.log(str); //输出:To天 is a beautiful 天, 天 天 happy! reg = /(to)?day/gi; reg.compile(reg); str = str.replace(reg, '日'); console.log(str); //输出:日 is a beautiful 日, 日 日 happy! </script>
8. Application régulière.
Les expressions régulières sont également appelées expressions régulières, donc lors de l'écriture, le processus est le même que pour l'écriture de JS, réfléchissez d'abord puis écrivez. Le plus important est de comprendre les règles qu'il veut exprimer. Tout d'abord, regardez attentivement son apparence pour voir à quoi elle ressemble, c'est-à-dire dans quel format elle existe. Ensuite, écrivez l'expression selon ce format pour voir si elle peut le faire. Atteignez notre objectif attendu. Sinon, en fait, décrire le format directement comme une expression n'obtiendra pas l'effet escompté. Heureusement, notre cadre principal existe déjà, et nous avons seulement besoin de savoir ce qui n'a pas fonctionné si quelque chose ne répond pas aux attentes. , vous pouvez simplement apporter de légères modifications à partir de ce framework, et finalement ce sera une expression parfaite. Par exemple, si vous souhaitez écrire une expression régulière pour vérifier un numéro de téléphone mobile, tout le monde sait que le numéro de téléphone mobile est composé de 11 chiffres, tous des chiffres, et que le début est 1, suivi de 2 chiffres. En raison des différents opérateurs, il peut y avoir des chiffres. être plusieurs combinaisons, suivies de 8 Les chiffres sont n'importe quels nombres, on peut donc stipuler que le début doit être 1, les 2 chiffres suivants sont limités selon différentes combinaisons fournies par chaque opérateur, et enfin saisir 8 chiffres de n'importe quel nombre. De cette façon, même si le cadre principal est complété, il existe également des circonstances particulières pour les numéros de téléphone mobile. Par exemple, l'ajout de 86 devant le numéro de téléphone mobile peut toujours être utilisé lorsque cela est nécessaire, nous devons prendre en compte cette situation. , sinon l'utilisateur doit saisir son numéro de téléphone portable. Ajoutez 86 devant, puis cliquez sur Mentionner, et le système ouvrira une fenêtre. Ce que vous avez entré est de la laine, et le système ne le reconnaît pas. , il vous suffit donc de modifier le framework et de considérer cette situation. Entrez et vous avez terminé.
Il semble donc que les expressions régulières soient très simples, mais en fait elles sont assez difficiles. Pourquoi es-tu si diao ? En dernière analyse, c'est ce genre d'expression qui est difficile à comprendre pour les gens ordinaires. Quand je l'ai écrit, je savais exactement ce qu'elle exprimait. Au bout d'un moment, j'ai regardé en arrière et j'ai pensé : « Oh mon Dieu, pourquoi ne le fais-tu pas ? je le reconnais ? En fait, c'est un processus où la pratique rend parfait. Je me souviens d'un texte qui disait : « Une bonne mémoire n'est pas aussi bonne qu'une mauvaise plume. »