やすくて速い正規表現(Easy Regular Expression)

正規表現の構成と意味、ジャバスクリプト(Javascript)でのお扱い

正規表現というのは検索パータンを指定し記号で表示したものです。

このアイデアはアメリカの数学者であるスティーブンコールクリーン(Stephen Cole Kleene)が1950年代に正義した正規言語(Regular Language)と関係がありまして、正規表現の文法は1980年代にパール(Perl)言語から使い始まりました。

主に、検索や置換の作業に使われ、皆が使うほとんどのプログラミング言語の中でサポートされている文法です。

例えば、次の文章の中で特定した条件を検索するとき正規表現の力を借りることができます。

The greatest danger for most of us is not that our aim is too high and we miss it, but that it is too low and we reach it.

  • ‘too’ 文字列を検索する -> /too/
    The greatest danger for most of us is not that our aim is too high and we miss it, but that it is too low and we reach it.
  • 大、小文字を構わなく ‘t’ 文字を検索するとき -> /t/-gi
    The greatest danger for most of us is not that our aim is too high and we miss it, but that it is too low and we reach it.
  • ‘d’ から始まる単語及び文字のすべてを検索するとき -> /[d]\w+/-gi
    The greatest danger for most of us is not that our aim is too high and we miss it, but that it is too low and we reach it.
  • ‘ea’を含む単語を検索するとき -> /[a-z]*ea[a-z]*/-i
    The greatest danger for most of us is not that our aim is too high and we miss it, but that it is too low and we reach it

正規表現の文法テストは次のページが助かります。

regexr.com

青いバーExpressionの下のインプットには表現を入れ書き、その下のボックスには検索対象になるテキストを入れればオッケーです。

まず、表現の意味について少し勉強してからJavascriptで楽に使う方法を確認してみます。


1. 正規表現の記号

  • / . / → すべての文字
  • / + / → 一つ以上の文字
  • / ? / → 0または一つの文字
  • / ^ / → 該当の文字を除く
  • / / → 範囲指定
  • / * / → 0または一つ以上の文字
  • / [ ] / → グループセット
  • / { } / → 繰り返しの回数指定
  • / \ / → エスケープ
  • / \d / → すべての数字
  • / \D / → 数字ではない文字
  • / \w / → アルファベット、数字、アンダーバー
  • / \W / → アルファベット、数字、アンダーバーではない文字
  • / \s / → 空白文字
  • / \S / → 空白ではない文字
  • / \n / → 改行

基本的に正規表現を構成する記号は上記であり、この記号さえ覚えれば正規表現の基本的な仕組みは十分理解できると思います。

それではサンプルを参考しながら正規表現を分析していきましょう。


2. 記号の意味

正規表現で一番基礎になる記号であるので覚えておくと役に立ち部分です。

dは数字、 wは英文字、 sは空白、 nは改行の意味であり、各文字の大文字は NOT(反対)を示します。

  • 数字が一回以上繰り返す文字検索 ? / [\d]+ /
  • 数字ではない文字を検索(空白含む) ? / [\D] /
  • 括弧(かっこ)が含まれている文字、数字検索 ? / \([\w]+\) /
  • 英文字、数字、アンダーバーではない文字検索 ? / [\W]+ /
  • 空白検索 ? / \s /

上で紹介したregexr.comで検索対象になるテキストをいれてパータンを直接に作成してみるとすぐ正規表現になれると思います。


大体エスケープというのは何ですかね。

キーボードの一番左一番上のキー(ESCと書かれているもの)がエスケープの略字です。エスケープの意味は逃れるのです。

こちらでは約束から逃れることだと認識すればいいと思いです。

例えば、正規表現で点(. dot)はすべての文字の意味にしようと約束した特別な文字ですね。

でもたまには’それ約束以外’に点をピリオド意味の点で使いたいときがありますね。

この時エスケープを使います。

エスケープを使わない限り、文字は約束した機能として動きます。

下の例で確認しましょう。

  • すべての文字を意味。 a, b, c, d, ?, !, @, , などすべての文字の中一つ ? / . /
  • エスケープを前につけるとただピリオド記号だけの意味 ? / \. /
  • 一つ以上の文字の意味 ? / + /
  • エスケープを前につけるとただプラス記号だけの意味 ? / \+ /

エスケープはよく使われる必須な機能であるので、そのままの文字にはならないのでエスケープが必要な特別な文字(. ? + *など)を覚えておく必要があります。

その文字の一部を下で紹介します。


上で紹介した特別な記号は特別な意味を持ちます。代表的な使い方は下の通りです。

  • 小文字a~z或は大文字A~Z中一致する英文字検索 ? / [a-zA-Z] /
  • color或はcolourを検索(‘u’があってもなくてもいい) ? / colou?r /
  • ‘a’を含む単語検索 ? / [\S]*a[a-zA-Z]* /
  • a-z或は空白(space)ではない文字検索 ? / [^a-z ] /
  • 英文字から始め必ず数字または特殊記号を含む文字列を検索 ? / [a-zA-Z]+[\d!@#\$%\^*]+[\w]* /

中括弧は繰り返しの回数を指定し、これを使うと詳しい検索パータンの設定が可能です。

中括弧の中、一つの数字は全体の繰り返し回数の意味であり、二つの数字は頭と末の範囲を指定する意味です。

例え、{3}は3回繰り返しの意味であり、{1, 3}は範囲区間1から3の意味です。

{1, }のような表現も可能であり、これは+記号と同様な意味, つまり一つ以上の意味であります。

  • Aが3回繰り返す文字を検索 ? / [A]{3} /
  • 3桁以上の数字を検索(3번以上繰り返し) ? / [\d]{3,} /
  • Aが二回から三回まで繰り返す文字を検索(繰り返す範囲指定) ? / [A]{2,3} /

繰り返し回数だけを指定すれば、意図しない結果が出ることがあるので、部分式(subexpression)と全後方一致検索を一緒に使うともっと効果的なパータンが可能です。


角かっこの中に記号を書き、検索パータンを指定します。直接に記号を書き込んだり、範囲指定記号の’-‘を使ってパータンを指定します。

  • 指定する文字(a 或は A 或は b 或は B 或は c 文字)一つを検索 ? [aAbBc]
  • aから eまで(a,b,c,d,e)の範囲内一つの文字を検索 ? [a-e]
  • 除外文字’^’があるので a-z 範囲に入らない文字(記号)を一つ検索 ? [^a-z]
  • 明記した記号の一つを検索 ? [?!@#$%^&*()_]
  • c或はCから始め, a-zの中一つで終わる単語検索 ? [cC][a-z]

[ ]を使うと一つだけの文字を検索するので一つ以上の文字を検索したいときには+記号(一つ以上の文字、0は不可)または*(0または一つ以上の文字、0も可能)記号を付ける必要があります。

  • 一つ以上のa-z文字を検索(必ず一つ以上の条件) ? [a-z]+
  • 0個以上のA-Z文字を検索(なくてもいい) ? [A-Z]*

特定構成で成り立つ文字列の検索に効果的であり、例えばメールアドレスの文字列があります。

メールアドレス形式のabcd@xyz.comを検索するためには次のようなパータンを使うことができます。

/[a-zA-Z0-9]+@[a-zA-Z0-9]+\.[a-zA-Z]+/

では、パータンを分析してみます。

先、メールのIDになる部分は英文字と数字が入るので、[a-zA-Z0-9]で始め一つ以上を意味する+記号を付けます。

その次、@が続きますので@を入れ, ホストのアドレスも一つ以上の英文字と数字を指定します。

.com形のアドレスなのでピリオドをそのまま使うためエスケープを使いまた一つ以上の英文字を入れてパータンを生成します。

もしIDの中に点が入るかホストアドレスが.co.jpなどの形になるときにはそれに対する応用が必要です。



3. まとめ

正規表現の第一印象はまるで外界言語みたいな姿で広く使われる既存の言語とは違う様子をしてますね。

軽く近づけられなくいやな気持がするときもありますが、少しだけでも調べてみたら意外と簡単な規則でパワフル機能を持っていいやつだなと思われる時が来るかもですね。

上で紹介した部分は代表的な機能を紹介するため簡単なパータンたちなので、上のパータンは不完全なパータンであります。もの正しいパータンを作るためにはもっと深く勉強する必要がありますね。

ご質問がありましたら、自由にコメントください!

あざーす!

자바스크립트, 문자열 함수 마음대로 다루기(Javascript works with string)

Javascript String Methods : slice(), substring(), includes(), startsWith(), trim(), replace()…….

프론트엔드(React, Vue, Angular)와 백엔드(Node.js)가 모두 자바스크립트를 사용하는 시대에 들어서니 자바스크립트의 기초가 몇 배는 더 중요해진 것 같습니다.

자바스크립트의 문법 중 문자열(String) 관련 함수는 참 쓸 일이 많으면서도 가볍게 여겨져 쉽게 외워지지 않는 슬픈 함수 중 하나인 것 같습니다.?

문자열 관련 함수는 한 번 외워두면 반복 학습할 일이 많으므로 첫 단추만 잘 꿰면 될 것 같습니다.

그럼 문자열 관련하여 사용 빈도가 높은 함수인 slice(), substring(), includes(), startsWith(), trim(), replace(), indexOf(), test() 함수에 대해 알아보겠습니다.


slice 함수는 위치(인덱스)를 매개변수로 전달하여 원하는 문자열을 추출합니다.

시작 위치(필수)와 종료 위치(옵션)를 전달하며, 시작 위치만 전달하는 경우 시작 위치부터 문자열 끝까지 추출합니다.

0은 순방향으로 첫 번째(앞에서 1번 째), 음수인 -1은 역방향으로 첫 번째(뒤에서 1번 째)를 나타냅니다.

const myWord = 'primavera';

myWord.slice(0,5);   // 'prima'
myWord.slice(-4);    // 'vera'
myWord.slice(3);     // 'mavera'

myWord.slice(3,1);    // '' -> slice함수는 시작인덱스가 종료인덱스보다 크면 빈 값을 반환

substring 함수도 slice와 마찬가지로 위치(인덱스)를 매개변수로 전달하여 원하는 문자열을 추출합니다.

기본적인 작동 방식은 같으나 slice와 달리 시작인덱스가 종료인덱스보다 큰 경우에는 자동으로 두 값의 위치를 변경하여 함수를 실행합니다.

또한 substring은 매개변수에 음수를 넣으면 이 값을 0으로 인식합니다.

따라서 (3, -1)을 전달하게 되면 (3, 0)으로 인식합니다. (3, 0)은 시작 인덱스가 더 크므로 위치를 변환하여 (0, 3)의 값을 계산한 결과를 반환합니다.

함수명이 비슷한 substr의 경우 ‘위치’와 ‘추출할 문자의 수’를 파라미터로 전달하는 함수입니다.

const myWord = 'invierno';

myWord.substring(3);     // 'ierno'
myWord.substring(1,3);   // 'nv'

myWord.substring(3,1);   // 'nv' -> 시작이 종료인덱스보다 크면 자동으로 위치 변경 후 함수를 실행

myWord.substring(3,-1);  // 'inv' -> 음수는 0, 시작인덱스와 종료인덱스는 위치 변경 후 계산

myWord.substring(-4);    // 'invierno' -> 음수는 0, 시작인덱스만 전달하여 0부터 끝까지 계산

myWord.substr(1,3);      // 'nvi' -> 시작인덱스 1부터 문자 3개를 추출

includes 함수는 지정한 문자열의 포함 여부를 확인합니다.

매개변수로 문자열(필수)과 위치인덱스(옵션)를 전달하며, 반환값은 true 또는 false 입니다.

문자열 내 지정 문자열 확인은 물론 배열 내 빈 값을 확인하는 용도로도 사용할 수 있습니다.

const myWord = 'alegria';

myWord.includes('le');       // true
myWord.includes('i');        // true
myWord.includes('rio');      // false

const myArray = ['dream','','king'];
myArray.includes('');       // true   -> myArray[1]이 빈 값이므로 true. 배열 내 빈 값을 찾는 용도
myArray.includes('dream');  // true   -> myArray[0]의 값과 일치
myArray.includes('drea');   // false  -> 배열 내 값과 정확하게 일치하는 경우에만 true를 반환

startsWith 함수도 문자열의 포함 여부를 확인하는 함수라고 볼 수 있습니다.

startsWith와 함께 문자열만 전달하면 0 인덱스부터 시작하여 해당 문자열의 존재 여부를 확인하여 BOOLEAN 값을 반환합니다.

마찬가지로 endsWith는 끝나는 문자열을 비교합니다.

const mySentence = 'cafe con leche';

mySentence.startsWith('cafe');     // true
mySentence.startsWith('fe');       // false
mySentence.startsWith('fe',2);     // true

mySentence.endsWith('eche');      // true

trim 함수는 문자열 양 끝의 공백을 제거하는 함수입니다.

문자열 변환 과정이나 실수로 보이지 않는 공백을 삽입하게 되면 문자열 비교 시 겉으로는 동일하지만 결과는

false를 반환하므로 이 trim 함수를 사용하면 문제를 해결할 수 있습니다.

문자열 양 끝의 공백, 탭, 줄바꿈 문자(/n)를 제거하며, 문자열 양 끝이 아닌 부분에는 효과가 없습니다.

const mySentence = '  la casa de papel   ';

mySentence.trim();        // 'la casa de papel'

const mySentence = ' el amor \n';

mySentence.trim();       // 'el amor'

replace 함수는 문자열에서 변경할 기존문자를 찾아 변경문자로 변경합니다.

문자열에서 매개변수로 전달한 기존문자가 여러 개 존재하는 경우 첫 번째로 탐색한 문자열만 변경합니다.

예를 들어, ‘my_name_is_Ron’에서 ‘_’문자를 모두 공백으로 변경하기 위해 replace(‘_’,’ ‘) 함수를 사용해도 결과는 ‘my name_is_Ron’이 반환됩니다.

따라서 문자열에서 해당하는 문자를 모두 변경하고 싶은 경우에는 정규표현식을 사용하면 됩니다.

정규표현식 관련 포스트

const mySentence = 'my name is:Ron';

mySentence.replace(':',' ');        //'my name is Ron'

const mySentence = 'my_name_is_Ron';

mySentence.replace('_',' ');      // 'my name_is_Ron'

//정규표현식 사용해서 모든 문자 변경하기
mySentence.replace(/_/g, ' ');    // 'my name is Ron'

indexOf 함수는 전달하는 문자의 위치를 반환합니다.

이 기능은 원하는 문자열의 앞 또는 뒤에서 문자열을 자를 때 유용하게 사용할 수 있습니다.

예를 들어, ‘Z001-03’이라는 문자열에서 뒤의 ’03’만 추출하고 싶은 경우 indexOf로 ‘-‘ 문자열의 위치를 찾고 해당 위치의 +1 위치부터 끝까지 문자열을 잘라내면 원하는 값을 가져올 수 있습니다.

만약 같은 문자가 여러 개 존재하는 경우 첫 번째 문자의 위치를 반환하며, 두 번째 매개변수에 시작 위치를 전달하면 시작 위치부터 검색을 시작합니다(시작 위치를 전달하더라도 반환 값은 전체 문자열에서 몇 번째인지를 전달합니다).

뒤에서부터 검색을 시작하는 함수는 lastIndexOf이며, 뒤에서부터 첫 번째로 나오는 문자의 위치를 반환합니다(반환 값은 해당 문자가 앞에서부터 몇 번째인지를 반환합니다).

전달하는 문자가 존재하지 않으면 -1을 반환합니다.

const mySentence = 'Z001-03';

mySentence.indexOf('-');     // 4

mySentence.substring(mySentence.indexOf('-')+1);    // '03'

mySentence.indexOf('D');    // -1

const mySentence = 'Z001-03-02';

mySentence.indexOf('-');    // 4
mySentence.indexOf('-', 5);   // 7

mySentence.lastIndexOf('-')   // 7

test 함수는 정규표현식과 함께 사용하여 매개변수로 전달하는 문자열의 유효성을 확인합니다.

비밀번호에 알파벳, 특수문자 등 포함여부 확인이나 생년월일 형식에 알파벳 등 불필요한 문자의 포함 여부를 효과적으로 확인할 수 있습니다.

const mySentence = /a/;
const myWord = 'baby';

mySentence.test(myWord);   // true

/\d/.test('cookie');       // false \d는 숫자(0~9)를 나타내는 정규표현식
/\d/.test('cookie1');      // true

const typedError = /[ㄱ-ㅎㅏ-ㅣ]/gi;
const inputData = '김ㅊ치와 나물';

typedError.test(inputData);    // true 

이 외에도 다양한 함수들이 많고 또 여러 요구에 맞춰 새롭고 편리한 함수가 계속 나오기 때문에 끊임없는 관심과 업데이트 관련하여 모니터링이 필요할 것 같습니다.