정규 표현식은 어떤 상황에서 사용해야될까?

Tori·2024년 11월 15일

JavaScript

목록 보기
2/7
post-thumbnail

mdn 정규 표현식 참고
모던 자바스크립트 Deep Dive 참고

정규 표현식을 학습하게된 이유

codewars의 DNA to RNA Conversion 문제를 풀면고 다른 사람은 어떻게 해결했나 코드를 비교해보았다.

알고리즘 문제 해결은 프로그래밍 실력을 향상시키는 데 중요한 역할을 한다.

문제 해결 능력을 향상시키기 위해 코드워스에서 문제들을 풀고있는데 다른 사람들이 문제를 해결할 때 어떤 방식으로 접근하는지 비교해보면서 다양한 풀이방법과 각각의 풀이법에 장단점을 생각하려고 하고 있다.

그중 문제를 해결할 때 반복문과 조건문을 사용하지 않고 정규 표현식을 사용하여 간단하게 작성한 코드들을 보면서 정규 표현식에 대해서 학습하고 잘 활용할 수 있어야겠다고 느꼈다.

문제

Deoxyribonucleic acid, DNA is the primary information storage molecule in biological systems.
It is composed of four nucleic acid bases Guanine ('G'), Cytosine ('C'), Adenine ('A'), and Thymine ('T').


Ribonucleic acid, RNA, is the primary messenger molecule in cells. RNA differs slightly from DNA its chemical structure and contains no Thymine.
In RNA Thymine is replaced by another nucleic acid Uracil ('U').


Create a function which translates a given DNA string into RNA.


For example:
"GCAT" => "GCAU"
The input string can be of arbitrary length - in particular, it may be empty.
All input is guaranteed to be valid, i.e. each input string will only ever consist of 'G', 'C', 'A' and/or 'T'.


정규 표현식을 사용하지 않고 reduce 사용하여 문제 해결 - 내가 푼 방법

// 문자열을 순회하면서 T -> U로 변환
export function DNAtoRNA(dna: string): string {
  return dna.split('').reduce((acc, cur) => acc += cur === 'T' ? 'U' : cur, '');
}

정규 표현식을 사용하여 문제 해결

replace

export function DNAtoRNA(dna: string): string {
  return dna.replace(/T/g, 'U');
}

replaceAll

export function DNAtoRNA(dna: string): string {
  return dna.replaceAll('T', 'U');
}





정규 표현식(regular expression, regexp)이란?

정규 표현식은 특정한 패턴을 가진 문자열의 집합을 표현하기 위해 사용하는 형식 언(formal language)어다. 정규표현식은 자바스크립트의 고유 문법이 아니며, 대부분의 프로그래밍 언어와 코드 에디터에도 내장되어 있다. 자바스크립트는 펄(Perl)의 정규 표현식 문법을 ES3부터 도입했다.

정규 표현식은 문자열을 대상으로 패턴 매칭 기능을 제공한다.
패턴 매칭 기능: 특정 패턴과 일치하는 문자열을 검색하거나 추출 or 치환할 수 있는 기능

예를 들어서 사용자로부터 입력받은 휴대폰 번호가 유요한 번호인지 체크하는 경우를 생각해보면, 휴대폰 번호는 "숫자 3개" + "-" + "숫자 4개" + "-" + "숫자 4개"라는 일정한 패턴이 있다.

휴대폰번호 패턴을 아래 정규 표현식으로 정의하고 사용자로부터 입력받은 문자열이 이 휴대폰번호 패턴에 매칭하는지 체크할 수 있다.

// 사용자로부터 입력받은 휴대폰번호
const tel = "010-1234-567삼";

// 정규 표현식 리터럴로 휴대폰번호 패턴을 정의
const regExp = /^\d{3}-\d{4}-\d{4}$/;

// tel이 휴대폰번호 패턴에 매칭하는지 테스트(확인)
regExp.test(tel); // false

정규 표현식을 사용하지 않는다면 반복문과 조건문을 통해 '첫 번째 문자가 숫자이고 이어지는 문자도 숫자이고 다음은 '-'이고..'와 같이 한 문자씩 연속해서 체크해야 한다.



정규 표현식의 생성

정규 표현식 객체(RegExp)를 생성하는 방법은 정규 표현식 리터럴과 RegExp 객체의 생성자 함수 호출을 사용하는 방법이 있다.

일반적인 방법은 정규 표현식 리터럴을 사용한다.

Flag

  • 정규 표현식은 전역 탐색이나 대소문자 무시와 같은 특성을 지정하는 플래그를 가질 수 있다.
  • 플래그는 단독으로 사용할 수도 있고, 순서에 상관없이 여러개를 지정할 수도 있다.
플래그설명대응하는 속성
d부분 문자열 일치에 대해 인덱스 생성RegExp.prototype.hasIndices
g전역 탐색RegExp.prototype.global
i대소문자를 구분하지 않음RegExp.prototype.ignoreCase
m여러 줄에 걸쳐 탐색RegExp.prototype.multiline
s개행 문자가 .과 일치함RegExp.prototype.dotAll
u"unicode" 패턴을 유니코드 코드 포인트의 시퀀스로 간주함RegExp.prototype.unicode
y"접착" 탐색, 대상 문자열의 현재 위치에서 탐색을 시작함RegExp.prototype.sticky



정규 표현식 리터럴

  • 슬래시로 패턴을 감싸서 작성한다.
  • 정규 표현식 리터럴은 스크립트를 불러올 때 컴파일되므로, 바뀔 일이 없는 패턴의 경우 리터럴을 사용하면 성능이 향상될 수 있다.
    / regexp / i
    /: (왼쪽) 시작 기호
    regexp: 패턴 (pattern)
    i: 플래그 (flag)
    /: (오른쪽) 종료 기호
const re = /ab+c/;

RegExp 객체의 생성자 함수 호출

  • 생성자 함수를 사용하면 정규 표현식이 런타임에 컴파일 된다.
  • 바뀔 수 있는 패턴이나, 사용자 입력 등 외부 출처에서 가져오는 패턴의 경우 RegExp를 사용하자.
new RegExp(pattern[, flags])
  • pattern: 정규 표현식의 텍스트
  • flags: 정규표현식의 플래그 (g, i, m, u, y)
const regExp = new RegExp("ab+c");

RegExp 메서드

RegExp.prototype.exec

  • exec 메서드는 인수로 전달받은 문자열에 대해 정규 표현식의 패턴을 검색하여 매칭 결과를 배열로 반환한다.
  • 매칭 결과가 없는 경우 null을 반환
const target = 'Is this all there is?';
const regExp = /is/;

const res = regExp.exec(target);
console.log(res); // [ 'is', index: 5, input: 'Is this all there is?' ]

  • exec 메서드는 문자열 내의 모든 패턴을 검색하는 g 플래그를 지정해도 첫 번째 매칭 결과만 반환한다.
const target = 'Is this all there is?';
const regExp = /is/g;

const res = regExp.exec(target);
console.log(res); // ['is', index: 5, input: 'Is this all there is?']

RegExp.prototype.test

  • test 메서드는 인수로 전달받은 문자열에 대해 정규 표현식의 패턴을 검색하여 매칭 결과를 불리언 값으로 반환한다.
const target = 'Is this all there is?';
const regExp = /is/;

const res = regExp.test(target);
console.log(res); // true

String.prototype.match

  • String 표준 빌트인 객체가 제공하는 match 메서드는 대상 문자열과 인수로 전달받은 정규표현식과의 매칭 결과를 배열로 반환한다.
const target = 'Is this all there is?';
const regExp = /is/;


target.match(regExp); // ['is', index: 5, input: 'Is this all there is?']

exec 메서드는 문자열 내의 모든 패턴을 검색하는 g 플래그를 지정해도 첫 번째 매칭 결과만 반환되는데 String.prototype.match 메서드는 g 플래그가 지정되면 모든 매칭 결과를 배열로 반환한다.

const target = 'Is this all there is?';
const regExp = /is/g;


target.match(regExp); // (2) ['is', 'is']

String.prototype.matchAll

캡처 그룹을 포함해서 모든 일치를 담은 반복기를 반환한다.


String.prototype.search

문자열에서 일치하는 부분을 탐색한다. 일치하는 부분의 인덱스, 또는 일치가 없는 경우 -1을 반환한다.


String.prototype.replace

문자열에서 일치하는 부분을 탐색하고, 그 부분을 대체 문자열로 바꾼다.


String.prototype.replaceAll

문자열에서 일치하는 부분을 모두 탐색하고, 모두 대체 문자열로 바꾼다.


String.prototype.split

정규 표현식 또는 문자열 리터럴을 사용해서 문자열을 부분 문자열의 배열로 나눈다.



정규 표현식을 사용하는 것이 좋은 상황

정규 표현식(Regular Expressions, regex)은 문자열 처리에서 매우 유용한 도구다.
그러나 사용해야 할 상황과 그에 따른 성능 고려 사항이 있다.
아래는 정규 표현식을 사용하는 것이 좋은 상황과 관련된 개념들에 대한 설명이다.

정규 표현식을 사용하는 것이 좋은 상황

1. 패턴 매칭:

특정 패턴을 가진 문자열을 찾거나 추출할 때 유용하다.
예를 들어, 이메일 주소, 전화번호, URL 등을 검증할 때 정규 표현식이 효과적이다.

2. 복잡한 문자열 조작:

문자열에서 특정 패턴을 찾아 대체하거나 삭제할 때 유용하다.
예를 들어, HTML 태그를 제거하거나 특정 형식의 데이터를 변환할 때 사용할 수 있다.

3. 유효성 검사:

사용자 입력의 형식을 검증할 때 정규 표현식을 사용하여 입력이 특정 형식(예: 우편번호, 신용카드 번호 등)을 따르는지 확인할 수 있다.

4. 다양한 문자열 처리:

여러 조건을 동시에 처리해야 할 때, 정규 표현식은 간결하게 표현할 수 있다.
예를 들어, 여러 종류의 구분자를 가진 문자열을 분리할 때 유용하다.



성능 고려 사항

1. 시간 복잡도:

정규 표현식의 성능은 패턴의 복잡성에 따라 달라진다.
간단한 패턴은 O(n) 시간 복잡도를 가지지만, 복잡한 패턴(예: 백트래킹이 필요한 경우)은 O(n^2) 또는 그 이상이 될 수 있다.
따라서 복잡한 정규 표현식은 성능 저하를 초래할 수 있다.

2. 메모리 사용:

정규 표현식은 메모리를 많이 사용할 수 있다.
특히, 큰 문자열을 처리하거나 복잡한 패턴을 사용할 때 메모리 사용량이 증가할 수 있다.

3. 가독성:

정규 표현식은 복잡할 수 있으며, 다른 개발자나 나중에 코드를 유지보수할 때 이해하기 어려울 수 있다.
따라서 간단한 문자열 조작에는 정규 표현식 대신 다른 방법을 사용하는 것이 좋다.



정규 표현식이 성능에 미치는 영향을 줄이기 위한 방법?

단순한 패턴 사용

: 가능한 한 간단한 정규 표현식을 사용하여 성능을 최적화한다.

미리 컴파일

: 정규 표현식을 미리 컴파일하여 재사용하면 성능을 향상시킬 수 있다.

대체 방법 고려

: 정규 표현식이 필요 없는 경우, 문자열 메서드(예: indexOf, split, replace)를 사용하는 것이 더 효율적일 수 있다.

테스트 및 프로파일링

: 정규 표현식을 사용하기 전에 성능을 테스트하고 프로파일링하여 최적의 방법을 찾는다.


정규 표현식은 강력한 도구이지만, 적절한 상황에서 사용하고 성능을 고려하는 것이 중요하다.



정리

  • 문자열 내부에 패턴과 일치하는 부분이 존재하는지만 알아내려면 test()나 search() 메서드를 사용
  • 더 느리더라도 일치에 관한 추가 정보가 필요하면 exec()과 match() 메서드를 사용
  • 일치하는 부분이 존재하면 exec()과 match는 일치에 관한 데이터를 포함한 배열을 반환하고, 일치에 사용한 정규 표현식 객체의 속성을 업데이트한다.
  • 일치하지 못한 경우 null을 반환한다. (null은 조건 평가 시 false와 같다.)
  • 정규 표현식은 강력한 도구이지만, 사용해야 할 상황과 성능을 고려해야 한다. 간단한 패턴 매칭이나 문자열 조작에는 매우 유용하지만, 복잡한 패턴이나 대량의 데이터를 처리할 때는 성능 저하가 발생할 수 있기때문에 정규 표현식을 사용할 때는 그 필요성과 성능을 항상 고려해야 한다.




profile
🌿

0개의 댓글