C++ 아이콘 제작자: Darius Dan - Flaticon
- 주어진 문자열이 주어진 규칙에 맞는지 확인할 때
- 주어진 문자열에서 원하는 패턴의 문자열을 검색할 때
- 주어진 문자열에서 원하는 패턴의 문자열로 치환할 때
^x // '^'은 문자열의 시작을 표현하며, x문자로 시작됨을 의미
x$ // '$'은 문자열의 종료를 표현하며, x문자로 종료됨을 의미
.x // '.'은 개행문자 \n을 제외한 다른 모든 문자를 의미
x+ // '+'은 1회 이상 반복을 의미, x문자가 1번 이상 반복됨을 의미 ({1,}과 동일)
x* // '*'은 0회 이상 반복을 의미, x문자가 0번 이상 반복됨을 의미 ({0,}과 동일)
x? // '?'은 0 or 1개 문자 매칭 의미, x문자가 존재할 수도 있고 안할수도 있다는 의미 ({0,1}과 동일)
x|y // '|'은 or를 표현, x 또는 y가 나온다는 의미
(x) // '()'은 그룹을 표현, 괄호로 묶인 패턴을 의미 ((abc){3}와 같이 사용해서 abcabcabc를 검출하는데 쓰임)
x{n} // '{}'은 반복을 의미, x가 n번 반복됨을 의미
x{n,} // '{,}'은 반복을 의미, x가 n번 이상 반복됨을 의미
x{n,m} // '{}'은 반복을 의미, x가 n번 이상 m번 이하로 반복됨을 의미
[xy] // '[]'은 x또는 y를 찾는다는 의미, [a-z0-9]이면 알파벳 소문자 또는 숫자를 찾는다는 의미
[^xy] // '[^]'은 not을 의미, x 및 y 를 제외하고 찾는다는 의미
[a-z] // '[-]'은 a ~ z 를 찾는다는 의미
\d // '\d'은 digit으로 숫자를 의미
\D // '\D'은 not digit으로 숫자를 제외하고 나머지 다른 문자를 의미
\s // '\s'은 space로 공백문자를 의미
\S // '\S'은 not space로 공백문자를 제외한 나머지 다른 문자를 의미
\t // '\t'은 tap을 의미
\w // '\w'은 알파벳 대문자,소문자와 숫자를 의미, [A-Za-z0-9]을 의미
\W // '\W'은 not \w, 즉 \w를 제외한 특수문자를 의미
(?:) // 캡쳐하지 않는 그룹 생성
예시로 해당 로그 파일은 db-(시간)-log.txt 형태로 생성된다.
여러가지 파일이 있는 폴더에서 위의 파일만 읽어낼 수 있을까?
위의 내용을 참고하여 표현하면 정규 표현식은 db-\d*-log\.txt 이다.여기서 \d*는 임의의 갯수의 숫자를 의마하는 것이고 .txt는 앞에 \를 붙인 이유는 문자로 해석하는걸 방지하기 위해서다.
std::vector<std::string> file_names = { "db-123-log.txt", "db-124-log.txt", "not-db-log.txt", "db-12-log.txt", "db-12-log.jpg" };
std::regex re("db-\\d*-log\\.txt");
for (const auto& file_name : file_names)
{
std::cout << file_name << ": " << std::boolalpha << std::regex_match(file_name, re) << '\n';
}
}
정규 표현식을 사용하기 위해 정규 표현식 객체를 정의해야 한다.
std::regex re("db-\\d*-log\\.txt");
참고로 정규 표현식 문법의 종류와, 정규 표현식을 처리하는 엔진 역시 여러가지 종류가 있는데
추가적으로 생성자에 인자를 전달할 수 있는데 예를 들어 grep 정규 표현식을 쓰고 싶다면
std::regex re("db-\\d*-log\\.txt", std::regex::grep);
처럼 전달하면 된다.
만약에 인자를 지정하지 않았다면 디폴트로std::regex::ECMAScript
가 들어간다.
std::regex re("db-\\d*-log\\.txt", std::regex::grep | std::regex::icase);
처럼 추가할 수 있다.
std::regex_match(file_name, re)
전달된 문자열과 정규 표현식 객체를 비교하여
표현식과 완전히 매칭이 된다면 true를 리턴한다.
위 표현식을 가져오면 "db-\d*-log\.txt" 에서 가운데 숫자만을 가져오고 싶다고 하면
"db-(\\d*)-log\\.txt"
이렇게 된다.이제 원하는 부분이 어디인지 표현을 했는데 이걸 어떻게 가져오냐??
std::smatch match; // 매칭된 결과를 string 으로 보관
for (const auto& number : phone_numbers) {
if (std::regex_match(number, match, re)) {
for (size_t i = 0; i < match.size(); i++)
{
std::cout << "Match : " << match[i].str() << std::endl;
}
smatch 객체를 선언하여 보관할 장소를 만들고,
regex_match함수에 비교할 문자열과 저장할 장소인 smatch객체를 넘긴후 정규 표현식을 전달한다.그러면 전달한 정규 표현식과 일치하는 문자열을 smatch객체에 저장한다.
match[index].str() 이렇게 저장된 문자열을 사용할 수 있다.
저장된 문자열은 위에서 캡처 그룹을 사용한 문자열이 저장되어 있음
문제는 while문에서 계속 찾았던 패턴을 계속 검색할 수 있다는 거다
그래서 해당 문자열을 업데잍트해서 검색된 패턴 바로 뒤 부터 다시 검색하게 만들어야 한다.
==> match.suffix();해당 함수를 사용하면 std::sub_match 객체를 리턴한다.
std::sub_match는 단순히 어떠한 문자열의 시작과 끝을 가리키는 반복자 2개를 가지고 있다.
이 때 suffix의 경우 원 문자열에서 검색된 패턴 바로 뒤 부터 문자열의 끝까지 해당하는 sum_match를 리턴한다이 때 string으로 변환하는 캐스팅 연산자가 있어 원래 문자열에 그냥 대입하면
기존 문자열에서 검색된 문자열이 제외된 문자열이 들어간다.
==> 그래서 검색된 문자열 다음 문자 부터 다시 검색을 시작한다.
정규 표현식의 반복자를 사용하면 좀 더 편리하게 검색할 수 있다.
==> 정규 표현식으로 매칭된 문자열들에 대한 반복자이다.
어떻게 반복자를 선언하냐??
std::sregex_iterator 우선 s가 붙어있는데 string을 사용하는 반복자이다.
생성자 호출함으로 생성할 수 있다.
1, 2번째 인자로 검색할 문자열의 시작과 끝을 전달하고(string의 begin(), end()함수 활용)
마지막 인자는 정규 표현식 객체를 전달하면 된다.
auto start = std::sregex_iterator(html.begin(), html.end(), re);
==> sregex_iterator가 알아서 패턴에 맞는 문자열을 뽑아서 start객체에 저장한다.
regex_iterator 의 경우 처음 생성될 때와, ++ 연산자로 증감 될 때마다
regex_search 를 통해 초기에 주어진 문자열 범위 내에서 패턴에 맞는 문자열을 검색합니다.
==> 알아서 검색한 문자열을 걸러주는 거지.그리하여 원하는 패턴들만 존재하는데 start->str() 이렇게 역참조로 접근이 가능하다.
캡처 그룹이 여러개 일 때 어떤게 어떤 캡처그룹인지 파악하냐?
캡처 그룹 () 괄호가 열린 순서대로 $1, $2 번호가 매겨진다.
std::regex_replace(str, re, "$1-sk-circle");
첫번째 인자로 원본 문자열을 2번 째 인자는 정규 표현식 객체를 마지막 인자는 원하는 패턴을 전달하면 된다.
개인 공부 기록용 블로그입니다.
틀린 부분 있으다면 지적해주시면 감사하겠습니다!!