C# - 정규 표현식 라이브러리 VerbalExpressions
소개
- 기존의 복잡한 방식의 정규식을 이해하기 쉽도로 해주는 라이브러리.
- 처음에는 JavaScript용으로 나왔지만 이후 대부분의 언어를 지원하고 있다.
- 소스 코드는 GitHub에 공개 되어 있다.
https://github.com/VerbalExpressions/CSharpVerbalExpressions - 간단 예제
[TestMethod]
public void TestingIfWeHaveAValidURL()
{
// Create an example of how to test for correctly formed URLs
var verbEx = new VerbalExpressions()
.StartOfLine()
.Then( "http" )
.Maybe( "s" )
.Then( "://" )
.Maybe( "www." )
.AnythingBut( " " )
.EndOfLine();
// Create an example URL
var testMe = "https://www.google.com";
Assert.IsTrue(verbEx.Test( testMe ), "The URL is incorrect");
Console.WriteLine("We have a correct URL ");
}
위의 코드를 기존의 정규식으로 표현하면 아래와 같다.
/^(http)(s)?(\:\/\/)(www\.)?([^\ ]*)$
- (2013.10.30)Nuget으로 설치한 라이브러리에 실제 소스 코드를 받아와서 빌드한 라이브러리에 있는 기능의 일부가 없음.
API
then(value)
- https://github.com/VerbalExpressions/JSVerbalExpressions/wiki/.then(-value-)
- 문자열에서 value와 같은 모든 것을 타겟으로 한다.
var my_paragraph = "Lorem ipsum. Dolor sit amet. Consectetur adipisicing elit.";
my_paragraph = VerEx().then( "." ).replace( my_paragraph, ". Stop." );
/* Outputs: Lorem ipsum. Stop. Dolor sit amet. Stop. Consectetur adipisicing elit. Stop. */
.endOfLine()
- 줄의 마지막을 가리킨다.
var lorem = "Lorem ipsum.\nDolor sit amet."
lorem = VerEx().endOfLine().replace( lorem, " Stop." );
/* Outputs: Lorem ipsum. Stop. Dolor sit amet. Stop. */
.endOfLine( enable )
- .endOfLine 적용 여부
var lorem = "Lorem ipsum\nLorem ipsum and something else";
var expression = VerEx().endOfLine().then( "Lorem ipsum" );
lorem = lorem.replace( expression , "First replacement" );
expression.endOfLine( false );
lorem = lorem.replace( expression , "Second replacement" );
/* Outputs: First replacement Then comes second replacement */
.startOfLine()
- 줄의 시작을 가리킨다.
var lorem = "Lorem ipsum\nDolor sit amet"
lorem = VerEx().startOfLine().replace( lorem, "Newline: " );
/* Outputs: Newline: Lorem ipsum Newline: Dolor sit amet */
var lineNumber = 1;
var lorem = "Lorem ipsum\nDolor sit amet"
lorem = VerEx().startOfLine().replace( lorem, function() { return lineNumber++ + ". "; } );
/* Outputs: 1. Lorem ipsum 2. Dolor sit amet */
var lorem = "Lorem ipsum\nAnother Lorem ipsum";
lorem = VerEx().startOfLine().then( "Lorem ipsum" ).replace( lorem, "This gets replaced" );
/* Outputs: This gets replaced Another Lorem ipsum */
.startOfLine( enable )
.maybe( value )
- value가 1회 or 0회 발견 되었다면
var my_paragraph = "Take a look at the link http://google.com and the secured version https://www.google.com";
var expression = VerEx()
.find( "http" )
.maybe( "s" )
.then( "://" )
.anythingBut( " " );
my_paragraph = expression.replace( my_paragraph, function(link) {
return "<a href='"+link+"'>"+link+"</a>";
} );
/* Outputs: Take a look at the link http://google.com and the secured version https://www.google.com */
.lineBreak(), .br()
var lorem = "Lorem ipsum.\r\nDolor sit\namet."
lorem = VerEx().lineBreak().replace( lorem, "<br>\n" );
/* Outputs: Lorem ipsum.
Dolor sit
amet. */
.range()
- Usage: .range( from, to [, from, to … ] ) Description: Add expression to match a tab character
VerEx().range( '0', '9', 'a', 'f')
C# 버전
- 내부적으로 C#의 정규표현식 클래스인 RegularExpressions을 사용하므로 이것에 대한 이해도 필요하다.
- RegexOptions 정규식 옵션
- http://msdn.microsoft.com/ko-kr/library/system.text.regularexpressions.regexoptions.aspx
BeginCapture
- 하나의 유닛으로 복수의 문자를 다루기 위해 Capture group를 만들기 위해 사용한다.
- http://neokido.tistory.com/m/post/view/id/307
- ((A)(B(C))) 은 아래의 문자열을 조사한다.
((A)(B(C)))
(A)
(B(C))
(C)
예제 코드
VerbalExpressions GetVerbalExpressions_TestMethod1()
{
var verbEx = VerbalExpressions.DefaultExpression
.StartOfLine()
.Then("test").WithOptions(System.Text.RegularExpressions.RegexOptions.IgnoreCase)
.EndOfLine();
return verbEx;
}
VerbalExpressions GetVerbalExpressions_TestMethod2()
{
var verbEx = VerbalExpressions.DefaultExpression
.StartOfLine()
.Then("test").WithOptions(System.Text.RegularExpressions.RegexOptions.IgnoreCase)
;
return verbEx;
}
// test 라는 단어 다음에 숫자가 나오는 경우
[TestMethod]
public void TestMethod1()
{
// test 라는 단어로 조사. test는 대문자 소문자 구분 안한다.
object[] range = new object[2] { 0, 9 };
var verbEx = GetVerbalExpressions_TestMethod1();
var testMe1 = "test0";
verbEx.Range(range);
Assert.IsTrue(verbEx.IsMatch(testMe1));
var verbEx2 = GetVerbalExpressions_TestMethod1();
var testMe2 = "test9";
verbEx2.Range(range);
Assert.IsTrue(verbEx2.IsMatch(testMe2));
var verbEx3 = GetVerbalExpressions_TestMethod1();
var testMe3 = "test8";
verbEx3.Range(range);
Assert.IsTrue(verbEx3.IsMatch(testMe3));
var verbEx4 = GetVerbalExpressions_TestMethod1();
var testMe4 = "tesst";
verbEx4.Range(range);
Assert.IsFalse(verbEx4.IsMatch(testMe4));
var verbEx5 = GetVerbalExpressions_TestMethod1();
var testMe5 = "testt0";
verbEx5.Range(range);
Assert.IsFalse(verbEx4.IsMatch(testMe5));
}
// test의 t 다음에 공백
[TestMethod]
public void TestMethod2()
{
// test 라는 단어로 조사. test는 대문자 소문자 구분 안한다.
var verbEx1 = GetVerbalExpressions_TestMethod2();
verbEx1.Add(" ");
var testMe1 = "test ";
Assert.IsTrue(verbEx1.Test(testMe1));
var verbEx2 = GetVerbalExpressions_TestMethod2();
verbEx2.Add(" ");
var testMe2 = "test fdf";
Assert.IsTrue(verbEx2.Test(testMe2));
var verbEx3 = GetVerbalExpressions_TestMethod2();
verbEx3.Add(" ");
var testMe3 = "test2";
Assert.IsFalse(verbEx3.Test(testMe3));
}
// test의 t 다음에 영어 이외의 문자 시작 조사
[TestMethod]
public void TestMethod3()
{
// test 라는 단어로 조사. test는 대문자 소문자 구분 안한다.
object[] range = new object[] { 'a', 'z', 'A', 'Z' };
var verbEx = GetVerbalExpressions_TestMethod1();
var testMe1 = "test_";
verbEx.AnythingBut(range.ToString());
Assert.IsTrue(verbEx.IsMatch(testMe1));
var verbEx2 = GetVerbalExpressions_TestMethod1();
var testMe2 = "testt";
verbEx2.AnythingBut(range.ToString());
Assert.IsFalse(verbEx2.IsMatch(testMe2));
var verbEx3 = GetVerbalExpressions_TestMethod1();
var testMe3 = "test2";
verbEx3.AnythingBut(range.ToString());
Assert.IsTrue(verbEx3.IsMatch(testMe3));
}
// 한글 포함 여부 체크
[TestMethod]
public void TestMethod4()
{
object[] range = new object[] { 'ㄱ', 'ㅎ', '가', '힣' };
var verbEx = GetVerbalExpressions_TestMethod1();
var testMe1 = "test아";
verbEx.Range(range);
Assert.IsTrue(verbEx.IsMatch(testMe1));
var verbEx2 = GetVerbalExpressions_TestMethod1();
var testMe2 = "test2T_";
verbEx2.Range(range);
Assert.IsFalse(verbEx2.IsMatch(testMe2));
}
- url 주소 형식이 맞는지 조사
var verbEx = VerbalExpressions.DefaultExpression
.StartOfLine()
.Then("http")
.Maybe("s")
.Then("://")
.Maybe("www.")
.AnythingBut(" ")
.EndOfLine();
var testMe = "https://www.google.com";
Assert.IsTrue(verbEx.Test(testMe));
- LineBreak가 포함되었는지 조사
//Arrange
var verbEx = VerbalExpressions.DefaultExpression;
string text = string.Format("testin with {0} line break", Environment.NewLine);
//Act
verbEx.LineBreak();
//Assert
Assert.IsTrue(verbEx.Test(text));
- tab이 포함되었는지 조사
//Arrange
var verbEx = VerbalExpressions.DefaultExpression;
string text = string.Format("text that contains {0} a tab", @"\t");
//Act
verbEx.Tab();
//Assert
Assert.IsTrue(verbEx.Test(text));
- 단어 개수 얻기
//Arrange
var verbEx = VerbalExpressions.DefaultExpression;
string text = "three words here";
int expectedCount = 3;
//Act
verbEx.Word();
Regex currentExpression = verbEx.ToRegex();
int result = currentExpression.Matches(text).Count;
//Assert
Assert.AreEqual(expectedCount, result);
- 이메일 형식이 맞는지 조사
public static readonly CommonRegex Url = new CommonRegex(1, @"((([A-Za-z]{3,9}:(?:\/\/)?)(?:[^-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(:[0-9]+)?|(?:www.|[^-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w_]*)?\??(?:[-\+=&;%@.\w-_]*)#?(?:[\w]*))?)");
public static readonly CommonRegex Email = new CommonRegex(2, @"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}");
var verbEx = VerbalExpressions.DefaultExpression;
verbEx.StartOfLine().Then(CommonRegex.Email);
var isMatch = verbEx.IsMatch("test@github.com");
Assert.IsTrue(isMatch, "Should match email address");
- url 형식이 맞는지 조사
public static readonly CommonRegex Url = new CommonRegex(1, @"((([A-Za-z]{3,9}:(?:\/\/)?)(?:[^-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(:[0-9]+)?|(?:www.|[^-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w_]*)?\??(?:[-\+=&;%@.\w-_]*)#?(?:[\w]*))?)");
public static readonly CommonRegex Email = new CommonRegex(2, @"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}");
var verbEx = VerbalExpressions.DefaultExpression;
verbEx.StartOfLine()
.Then(CommonRegex.Url);
Assert.IsTrue(verbEx.IsMatch("http://www.google.com"), "Should match url address");
Assert.IsTrue(verbEx.IsMatch("https://www.google.com"), "Should match url address");
Assert.IsTrue(verbEx.IsMatch("http://google.com"), "Should match url address");
- ‘test’와 ‘ing’가 연달아서 있는지 조사(Add를 사용)
var verbEx = VerbalExpressions.DefaultExpression;
verbEx.Add("test")
.Add("ing")
.StartOfLine();
string text = "testing1234";
Assert.IsTrue(verbEx.IsMatch(text), "Should match that the text starts with test");
var verbEx2 = VerbalExpressions.DefaultExpression;
verbEx2.Add("test")
.Add("ing")
.StartOfLine();
string text2 = "test123ing";
Assert.IsFalse(verbEx2.IsMatch(text2), "Should match that the text starts with test");
- 대소문자 구별 여부(WithAbyCase)
var verbEx = VerbalExpressions.DefaultExpression;
verbEx.Add("www")
.WithAnyCase();
var isMatch = verbEx.IsMatch("wWw");
Assert.IsTrue(isMatch, "Should match any case");
var verbEx = VerbalExpressions.DefaultExpression;
verbEx.Add("www")
.WithAnyCase(false);
var isMatch = verbEx.IsMatch("wWw");
Assert.IsFalse(isMatch, "Should not match any case");
- 대문자, 소문자 구별 안하는 옵션 설정(AddModifier)
VerbalExpressions verbEx = VerbalExpressions.DefaultExpression;
verbEx.Add( "teststring" ).AddModifier( 'i' );
Assert.IsTrue( verbEx.IsMatch( "TESTSTRING" ) );
- Capture grouping
// Arrange
VerbalExpressions verbEx = VerbalExpressions.DefaultExpression;
// Act
verbEx.BeginCapture()
.Add( "com" )
.Or( "org" )
.EndCapture();
// Assert
Assert.AreEqual( "((com)|(org))", verbEx.ToString() );
- 특정 단어로 끝나는지 조사(EndOfLine)
var verbEx = VerbalExpressions.DefaultExpression;
verbEx.Add(".com").EndOfLine();
var isMatch = verbEx.IsMatch("www.google.com");
Assert.IsTrue(isMatch, "Should match '.com' in end");
var verbEx = VerbalExpressions.DefaultExpression;
verbEx.Add(".com").EndOfLine();
var isMatch = verbEx.IsMatch("http://www.google.com/");
Assert.IsFalse(isMatch, "Should not match '/' in end");
- Multiple
//Arrange
var verbEx = VerbalExpressions.DefaultExpression;
string text = "testesting 123 yahoahoahou another test";
string expectedExpression = "y(aho)+u";
//Act
verbEx.Add("y")
.Multiple("aho")
.Add("u");
//Assert
Assert.IsTrue(verbEx.Test(text));
Assert.AreEqual(expectedExpression, verbEx.ToString());
- or 기법 사용
public void Or_AddComOrOrg_DoesMatchComAndOrg()
{
var verbEx = VerbalExpressions.DefaultExpression;
verbEx.Add("com").Or("org");
Console.WriteLine(verbEx);
Assert.IsTrue(verbEx.IsMatch("org"), "Should match 'org'");
Assert.IsTrue(verbEx.IsMatch("com"), "Should match 'com'");
}
[Test]
public void Or_AddComOrOrg_RegexIsAsExpecteds()
{
var verbEx = VerbalExpressions.DefaultExpression;
verbEx.Add("com").Or("org");
Assert.AreEqual("(com)|(org)", verbEx.ToString());
}
- 범위로 검색(한글, 영어, 숫자, 한문 검색할 때 유용)
[Test]
public void Range_WhenOddNumberOfItemsInArray_ShouldAppendLastElementWithOrClause()
{
//Arrange
var verbEx = VerbalExpressions.DefaultExpression;
string text = "abcd7sdadqascdaswde";
object[] range = new object[3] { 1, 6, 7 };
//Act
verbEx.Range(range);
//Assert
Assert.IsTrue(verbEx.IsMatch(text));
}
[Test]
public void Range_WhenOddNumberOfItemsInArray_ShouldAppendWithPipe()
{
//Arrange
var verbEx = VerbalExpressions.DefaultExpression;
object[] range = new object[3] { 1, 6, 7 };
string expectedExpression = "[1-6]|7";
//Act
verbEx.Range(range);
//Assert
Assert.AreEqual(expectedExpression, verbEx.ToString());
}
도움말
- JS 버전
- https://github.com/VerbalExpressions/JSVerbalExpressions/wiki
- Go 버전
- http://godoc.org/github.com/VerbalExpressions/GoVerbalExpressions
- 정규 표현식 표
- http://j07051.tistory.com/554
- 정규 표현식 강좌
- http://blog.eairship.kr/category/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%20%EA%B4%80%EB%A0%A8/%EC%A0%95%EA%B7%9C%20%ED%91%9C%ED%98%84%EC%8B%9D
이 글은 2019-03-30에 작성되었습니다.