[ジャンプ2 Python]Chapter 08:正規表現は?Python正規表現の基礎と応用
正規表現の表示
正規表現(Regular Expressions)は、Python固有の構文ではなく、複雑な文字列を処理する際に使用される方法です.正規表現の学習はPythonの学習とは別の分野の課題である.
🤔 正規表現が必要な理由 주민등록번호를 포함하고 있는 텍스트가 있다.
이 텍스트에 포함된 모든 주민등록번호의 뒷자리를 * 문자로 변경해 보자.
주민등록번호를 포함하고 있는 텍스트가 있다.
이 텍스트에 포함된 모든 주민등록번호의 뒷자리를 * 문자로 변경해 보자.
data = """
park 800905-1049118
kim 700905-1059119
"""
result = []
for line in data.split("\n"):
word_result = []
for word in line.split(" "):
if len(word) == 14 and word[:6].isdigit() and word[7:].isdigit():
word = word[:6] + "-" + "*******"
word_result.append(word)
result.append(" ".join(word_result))
print("\n".join(result))
🤯 頭に花が咲く◇正規表現を使うと、コードが非常に簡潔になります
正規表現の基礎、メタ文字
元文字とは、もともとその文字の意味ではなく、特殊な用途に用いられる文字のこと.. ^ $ * + ? { } [ ] \ | ( )
正規表現で上記のメタ文字を使用すると、特別な意味があります.
📌 文字クラス [abc]
# 문자 클래스를 만드는 메타 문자인 [ ] 사이에는 어떤 문자도 들어갈 수 있다.
. ^ $ * + ? { } [ ] \ | ( )
[abc]
# 문자 클래스를 만드는 메타 문자인 [ ] 사이에는 어떤 문자도 들어갈 수 있다.
Dot(.)
a.b # === "a + 모든문자 + b"
# 정규 표현식의 Dot(.) 메타 문자는 줄바꿈 문자인 \n을 제외한 모든 문자와 매치됨을 의미한다.
.
と一致するので、正規式と組み合わせます..
と一致するので、正規式と組み合わせます.繰り返し(*)
ca*t
# 이 정규식에는 반복을 의미하는 * 메타 문자가 사용되었다. 여기에서 사용한 *은 * 바로 앞에 있는 문자 a가 0부터 무한대로 반복될 수 있다는 의미이다.
# 메모리상 2억개로 제한
繰り返し(+)
ca+t # === "c + a(1번 이상 반복) + t"
# 반복을 나타내는 또 다른 메타 문자로 +가 있다. +는 최소 1번 이상 반복될 때 사용한다. 즉 *가 반복 횟수 0부터라면 +는 반복 횟수 1부터인 것이다.
繰り返し({m,n},?) ca{2}t # === "c + a(반드시 2번 반복) + t"
ca{2,5}t # === "c + a(2~5회 반복) + t"
ab?c # === "a + b(있어도 되고 없어도 된다) + c"
# === {0,1}
Pythonで正規表現をサポートするreモジュール >>> import re
>>> p = re.compile('ab*')
re.compileを使用して正規表現をコンパイルします(上記の例ではab*).re.compileで返されるオブジェクトp(コンパイルされたモードオブジェクト)を使用して、その後の操作を実行します.
すなわち,pは現在モードオブジェクトとなり,pをモードにマッチングさせるべきである.
match
matchメソッドは文字列の先頭から正規表現と一致するかどうかをチェックしますimport re
>>> p = re.compile('[a-z]+')
# 매치가 되는 경우
>>> m = p.match("python")
>>> print(m)
<re.Match object; span=(0, 6), match='python'> # 잘 매치됨
# 매치가 되지 않는 경우
>>> m = p.match("3 python")
>>> print(m)
None
search
一致するオブジェクトの検索>>> m = p.search("3 python")
>>> print(m)
<re.Match object; span=(2, 8), match='python'>
findall
一致するstringをリストに戻す>>> result = p.findall("life is too short")
>>> print(result)
['life', 'is', 'too', 'short']
finditer
一致するstringをイテレーションオブジェクト=イテレーション=ループ可能オブジェクトに戻す>>> result = p.finditer("life is too short")
>>> print(result)
<callable_iterator object at 0x01F5E390>
>>> for r in result: print(r)
<re.Match object; span=(0, 4), match='life'>
<re.Match object; span=(5, 7), match='is'>
<re.Match object; span=(8, 11), match='too'>
<re.Match object; span=(12, 17), match='short'>
以上の4つの方法で生成されるオブジェクトはmatchオブジェクトです
matchオブジェクトのメソッド
マッチングオブジェクト(=matchオブジェクト)で使用可能な方法
ca{2}t # === "c + a(반드시 2번 반복) + t"
ca{2,5}t # === "c + a(2~5회 반복) + t"
ab?c # === "a + b(있어도 되고 없어도 된다) + c"
# === {0,1}
>>> import re
>>> p = re.compile('ab*')
re.compileを使用して正規表現をコンパイルします(上記の例ではab*).re.compileで返されるオブジェクトp(コンパイルされたモードオブジェクト)を使用して、その後の操作を実行します.すなわち,pは現在モードオブジェクトとなり,pをモードにマッチングさせるべきである.
match
matchメソッドは文字列の先頭から正規表現と一致するかどうかをチェックします
import re
>>> p = re.compile('[a-z]+')
# 매치가 되는 경우
>>> m = p.match("python")
>>> print(m)
<re.Match object; span=(0, 6), match='python'> # 잘 매치됨
# 매치가 되지 않는 경우
>>> m = p.match("3 python")
>>> print(m)
None
search
一致するオブジェクトの検索
>>> m = p.search("3 python")
>>> print(m)
<re.Match object; span=(2, 8), match='python'>
findall
一致するstringをリストに戻す
>>> result = p.findall("life is too short")
>>> print(result)
['life', 'is', 'too', 'short']
finditer
一致するstringをイテレーションオブジェクト=イテレーション=ループ可能オブジェクトに戻す
>>> result = p.finditer("life is too short")
>>> print(result)
<callable_iterator object at 0x01F5E390>
>>> for r in result: print(r)
<re.Match object; span=(0, 4), match='life'>
<re.Match object; span=(5, 7), match='is'>
<re.Match object; span=(8, 11), match='too'>
<re.Match object; span=(12, 17), match='short'>
以上の4つの方法で生成されるオブジェクトはmatchオブジェクトですmatchオブジェクトのメソッド
マッチングオブジェクト(=matchオブジェクト)で使用可能な方法
コンパイルオプション
正規表現をコンパイルするときに使用できるオプション# 옵션 ㄴㄴ
import re
p = re.compile('[a-z]+')
# 옵션 넣으면?
import re
p = re.compile('[a-z]+', 옵션을 넣는곳)
DOTALL(S)
.
この改行文字も含めて、すべての文字と組み合わせることができます.# 기존
>>> import re
>>> p = re.compile('a.b')
>>> m = p.match('a\nb')
>>> print(m)
None
# DOTALL
>>> p = re.compile('a.b', re.DOTALL)
>>> m = p.match('a\nb')
>>> print(m)
<_sre.SRE_Match object at 0x01FCF3D8>
IGNORECASE(I)
大文字と小文字を問わず組み合わせることができます.>>> p = re.compile('[a-z]', re.I)
>>> p.match('python')
<_sre.SRE_Match object at 0x01FCFA30>
>>> p.match('Python')
<_sre.SRE_Match object at 0x01FCFA68>
>>> p.match('PYTHON')
<_sre.SRE_Match object at 0x01FCF9F8>
MULTILINE(M)
複数の線と組み合わせることができます.( ^
, $
メタ文字の使用に関するオプション)# 기존
import re
p = re.compile("^python\s\w+")
data = """python one
life is too short
python two
you need python
python three"""
print(p.findall(data)) # ['python one']
# MULTILINE(M)
import re
p = re.compile("^python\s\w+", re.MULTILINE)
data = """python one
life is too short
python two
you need python
python three"""
print(p.findall(data)) # ['python one', 'python two', 'python three']
VERBOSE(X)
verboseモードの使用を許可します.(正規表現の表示が容易になり、コメントなども使用できます.)charref = re.compile(r'&[#](0[0-7]+|[0-9]+|x[0-9a-fA-F]+);')
# 예시
charref = re.compile(r"""
&[#] # Start of a numeric entity reference
(
0[0-7]+ # Octal form
| [0-9]+ # Decimal form
| x[0-9a-fA-F]+ # Hexadecimal form
)
; # Trailing semicolon
""", re.VERBOSE)
簡単に言えば、長い正規表現にコメントを追加できます.
スラッシュ問題
Python式にはスラッシュで始まる式がたくさんあるので、スラッシュの後ろの文字列は開発者の意図とは異なる場合があります\section # === [ \t\n\r\f\v]ection
# \s 문자가 whitespace로 해석되어 의도한 대로 매치가 이루어지지 않는다.
\\section ## \section 으로 인식함
# \를 Raw String(= 특수문자를 무시한다) 이라 말해주면 됨
>>> p = re.compile(r'\\section')
強力な正規表現の世界に入ります。
📌 メタ文字
まだ表示されていないすべてのメタ文字について説明します.
|
|メタ文字の意味はorと同じです.A|Bの正規表現があれば、AまたはBを表す.>>> p = re.compile('Crow|Servo') # Crow 또는 Servo
>>> m = p.match('CrowHello')
>>> print(m)
<re.Match object; span=(0, 4), match='Crow'>
^
^メタ文字は、文字列の先頭に一致することを示します.>>> print(re.search('^Life', 'Life is too short'))
<re.Match object; span=(0, 4), match='Life'>
>>> print(re.search('^Life', 'My Life'))
None
$
^メタ文字とは逆です.すなわち、$は文字列の末尾に一致することを意味する.>>> print(re.search('short$', 'Life is too short'))
<re.Match object; span=(12, 17), match='short'>
>>> print(re.search('short$', 'Life is too short, you need python'))
None
\b
スペースを表すメタ文字>>> p = re.compile(r'\bclass\b')
>>> print(p.search('no class at all'))
<re.Match object; span=(3, 8), match='class'>
>>> print(p.search('the declassified algorithm'))
None
📌 ひっくり返す
()結合式の使用
繰り返しabcを探していたら?(ABC)+
>>> p = re.compile('(ABC)+')
>>> m = p.search('ABCABCABC OK?')
>>> print(m)
<re.Match object; span=(0, 9), match='ABCABCABC'>
>>> print(m.group())
ABCABCABC
# 그루핑중 원하는 그룹만 뽑아 올수있다
>>> p = re.compile(r"(\w+)\s+\d+[-]\d+[-]\d+")
>>> m = p.search("park 010-1234-1234")
>>> print(m.group(1))
park
# 그루핑된 문자열 재참조하기
>>> p = re.compile(r'(\b\w+)\s+\1') # (그룹) + " " + 그룹과 동일한 단어
# 재참조 메타 문자 \1, \1은 정규식의 그룹 중 첫 번째 그룹을 가리킨다.
>>> p.search('Paris in the the spring').group()
'the the'
# 그루핑된 문자열에 이름 붙히기 (?P<그룹명>...)
>>> p = re.compile(r"(?P<name>\w+)\s+((\d+)[-]\d+[-]\d+)")
>>> m = p.search("park 010-1234-1234")
>>> print(m.group("name"))
park
📌 ぜんぽうたんさく
肯定型:(?=)
検索には含まれますが、検索結果には含まれません.>>> p = re.compile(".+(?=:)") # 조건에선 찾되, 결과에선 빠지게 하고 싶을때. (?=:)는 :를 제외 한다는말
>>> m = p.search("http://google.com")
>>> print(m.group())
http
否定型:(?!)
検索から除外された出力import re
p = re.compile(".*[.](?!bat$|exe$).*$",re.M)
l = p.findall("""
autoexec.exe
autoexec.bat
autoexec.jpg
""")
print(l)
📌 置換文字列
正規式に合わせた部分を他の文字に簡単に変えることができます.>>> p = re.compile('(blue|white|red)') # 정규식
>>> p.sub('colour', 'blue socks and red shoes') # 위 정규식과 2번째 인자가 일치하는 부분을 1인자로 바꾼다
'colour socks and colour shoes'
📌 Greedy vs Non-Greedy import re
>>> s = '<html><head><title>Title</title>'
>>> len(s)
32
>>> print(re.match('<.*>', s).span()) # Greedy '<html><head><title>Title</title>' 최대에서 가장 첫번째<>안의 문자를 가져옴
(0, 32)
>>> print(re.match('<.*>', s).group()) # Non-Greedy '<html><head><title>Title</title>'<>가 최소로 가장 첫번째 문자를 가져옴
<html><head><title>Title</title>
Reference
この問題について([ジャンプ2 Python]Chapter 08:正規表現は?Python正規表現の基礎と応用), 我々は、より多くの情報をここで見つけました
https://velog.io/@dndb3599/점프-투-파이썬-Chapter-08-정규표현식이란-파이썬-정규표현식의-기초와-활용
テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol
# 옵션 ㄴㄴ
import re
p = re.compile('[a-z]+')
# 옵션 넣으면?
import re
p = re.compile('[a-z]+', 옵션을 넣는곳)
# 기존
>>> import re
>>> p = re.compile('a.b')
>>> m = p.match('a\nb')
>>> print(m)
None
# DOTALL
>>> p = re.compile('a.b', re.DOTALL)
>>> m = p.match('a\nb')
>>> print(m)
<_sre.SRE_Match object at 0x01FCF3D8>
>>> p = re.compile('[a-z]', re.I)
>>> p.match('python')
<_sre.SRE_Match object at 0x01FCFA30>
>>> p.match('Python')
<_sre.SRE_Match object at 0x01FCFA68>
>>> p.match('PYTHON')
<_sre.SRE_Match object at 0x01FCF9F8>
# 기존
import re
p = re.compile("^python\s\w+")
data = """python one
life is too short
python two
you need python
python three"""
print(p.findall(data)) # ['python one']
# MULTILINE(M)
import re
p = re.compile("^python\s\w+", re.MULTILINE)
data = """python one
life is too short
python two
you need python
python three"""
print(p.findall(data)) # ['python one', 'python two', 'python three']
charref = re.compile(r'&[#](0[0-7]+|[0-9]+|x[0-9a-fA-F]+);')
# 예시
charref = re.compile(r"""
&[#] # Start of a numeric entity reference
(
0[0-7]+ # Octal form
| [0-9]+ # Decimal form
| x[0-9a-fA-F]+ # Hexadecimal form
)
; # Trailing semicolon
""", re.VERBOSE)
\section # === [ \t\n\r\f\v]ection
# \s 문자가 whitespace로 해석되어 의도한 대로 매치가 이루어지지 않는다.
\\section ## \section 으로 인식함
# \를 Raw String(= 특수문자를 무시한다) 이라 말해주면 됨
>>> p = re.compile(r'\\section')
📌 メタ文字
まだ表示されていないすべてのメタ文字について説明します.
|
|メタ文字の意味はorと同じです.A|Bの正規表現があれば、AまたはBを表す.>>> p = re.compile('Crow|Servo') # Crow 또는 Servo
>>> m = p.match('CrowHello')
>>> print(m)
<re.Match object; span=(0, 4), match='Crow'>
^
^メタ文字は、文字列の先頭に一致することを示します.>>> print(re.search('^Life', 'Life is too short'))
<re.Match object; span=(0, 4), match='Life'>
>>> print(re.search('^Life', 'My Life'))
None
$
^メタ文字とは逆です.すなわち、$は文字列の末尾に一致することを意味する.>>> print(re.search('short$', 'Life is too short'))
<re.Match object; span=(12, 17), match='short'>
>>> print(re.search('short$', 'Life is too short, you need python'))
None
\b
スペースを表すメタ文字>>> p = re.compile(r'\bclass\b')
>>> print(p.search('no class at all'))
<re.Match object; span=(3, 8), match='class'>
>>> print(p.search('the declassified algorithm'))
None
📌 ひっくり返す
()結合式の使用
繰り返しabcを探していたら?(ABC)+
>>> p = re.compile('(ABC)+')
>>> m = p.search('ABCABCABC OK?')
>>> print(m)
<re.Match object; span=(0, 9), match='ABCABCABC'>
>>> print(m.group())
ABCABCABC
# 그루핑중 원하는 그룹만 뽑아 올수있다
>>> p = re.compile(r"(\w+)\s+\d+[-]\d+[-]\d+")
>>> m = p.search("park 010-1234-1234")
>>> print(m.group(1))
park
# 그루핑된 문자열 재참조하기
>>> p = re.compile(r'(\b\w+)\s+\1') # (그룹) + " " + 그룹과 동일한 단어
# 재참조 메타 문자 \1, \1은 정규식의 그룹 중 첫 번째 그룹을 가리킨다.
>>> p.search('Paris in the the spring').group()
'the the'
# 그루핑된 문자열에 이름 붙히기 (?P<그룹명>...)
>>> p = re.compile(r"(?P<name>\w+)\s+((\d+)[-]\d+[-]\d+)")
>>> m = p.search("park 010-1234-1234")
>>> print(m.group("name"))
park
📌 ぜんぽうたんさく
肯定型:(?=)
検索には含まれますが、検索結果には含まれません.>>> p = re.compile(".+(?=:)") # 조건에선 찾되, 결과에선 빠지게 하고 싶을때. (?=:)는 :를 제외 한다는말
>>> m = p.search("http://google.com")
>>> print(m.group())
http
否定型:(?!)
検索から除外された出力import re
p = re.compile(".*[.](?!bat$|exe$).*$",re.M)
l = p.findall("""
autoexec.exe
autoexec.bat
autoexec.jpg
""")
print(l)
📌 置換文字列
正規式に合わせた部分を他の文字に簡単に変えることができます.>>> p = re.compile('(blue|white|red)') # 정규식
>>> p.sub('colour', 'blue socks and red shoes') # 위 정규식과 2번째 인자가 일치하는 부분을 1인자로 바꾼다
'colour socks and colour shoes'
📌 Greedy vs Non-Greedy import re
>>> s = '<html><head><title>Title</title>'
>>> len(s)
32
>>> print(re.match('<.*>', s).span()) # Greedy '<html><head><title>Title</title>' 최대에서 가장 첫번째<>안의 문자를 가져옴
(0, 32)
>>> print(re.match('<.*>', s).group()) # Non-Greedy '<html><head><title>Title</title>'<>가 최소로 가장 첫번째 문자를 가져옴
<html><head><title>Title</title>
Reference
この問題について([ジャンプ2 Python]Chapter 08:正規表現は?Python正規表現の基礎と応用), 我々は、より多くの情報をここで見つけました
https://velog.io/@dndb3599/점프-투-파이썬-Chapter-08-정규표현식이란-파이썬-정규표현식의-기초와-활용
テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol
>>> p = re.compile('Crow|Servo') # Crow 또는 Servo
>>> m = p.match('CrowHello')
>>> print(m)
<re.Match object; span=(0, 4), match='Crow'>
>>> print(re.search('^Life', 'Life is too short'))
<re.Match object; span=(0, 4), match='Life'>
>>> print(re.search('^Life', 'My Life'))
None
>>> print(re.search('short$', 'Life is too short'))
<re.Match object; span=(12, 17), match='short'>
>>> print(re.search('short$', 'Life is too short, you need python'))
None
>>> p = re.compile(r'\bclass\b')
>>> print(p.search('no class at all'))
<re.Match object; span=(3, 8), match='class'>
>>> print(p.search('the declassified algorithm'))
None
()結合式の使用
繰り返しabcを探していたら?
(ABC)+
>>> p = re.compile('(ABC)+')
>>> m = p.search('ABCABCABC OK?')
>>> print(m)
<re.Match object; span=(0, 9), match='ABCABCABC'>
>>> print(m.group())
ABCABCABC
# 그루핑중 원하는 그룹만 뽑아 올수있다
>>> p = re.compile(r"(\w+)\s+\d+[-]\d+[-]\d+")
>>> m = p.search("park 010-1234-1234")
>>> print(m.group(1))
park
# 그루핑된 문자열 재참조하기
>>> p = re.compile(r'(\b\w+)\s+\1') # (그룹) + " " + 그룹과 동일한 단어
# 재참조 메타 문자 \1, \1은 정규식의 그룹 중 첫 번째 그룹을 가리킨다.
>>> p.search('Paris in the the spring').group()
'the the'
# 그루핑된 문자열에 이름 붙히기 (?P<그룹명>...)
>>> p = re.compile(r"(?P<name>\w+)\s+((\d+)[-]\d+[-]\d+)")
>>> m = p.search("park 010-1234-1234")
>>> print(m.group("name"))
park
📌 ぜんぽうたんさく
肯定型:(?=)
検索には含まれますが、検索結果には含まれません.>>> p = re.compile(".+(?=:)") # 조건에선 찾되, 결과에선 빠지게 하고 싶을때. (?=:)는 :를 제외 한다는말
>>> m = p.search("http://google.com")
>>> print(m.group())
http
否定型:(?!)
検索から除外された出力import re
p = re.compile(".*[.](?!bat$|exe$).*$",re.M)
l = p.findall("""
autoexec.exe
autoexec.bat
autoexec.jpg
""")
print(l)
📌 置換文字列
正規式に合わせた部分を他の文字に簡単に変えることができます.>>> p = re.compile('(blue|white|red)') # 정규식
>>> p.sub('colour', 'blue socks and red shoes') # 위 정규식과 2번째 인자가 일치하는 부분을 1인자로 바꾼다
'colour socks and colour shoes'
📌 Greedy vs Non-Greedy import re
>>> s = '<html><head><title>Title</title>'
>>> len(s)
32
>>> print(re.match('<.*>', s).span()) # Greedy '<html><head><title>Title</title>' 최대에서 가장 첫번째<>안의 문자를 가져옴
(0, 32)
>>> print(re.match('<.*>', s).group()) # Non-Greedy '<html><head><title>Title</title>'<>가 최소로 가장 첫번째 문자를 가져옴
<html><head><title>Title</title>
Reference
この問題について([ジャンプ2 Python]Chapter 08:正規表現は?Python正規表現の基礎と応用), 我々は、より多くの情報をここで見つけました
https://velog.io/@dndb3599/점프-투-파이썬-Chapter-08-정규표현식이란-파이썬-정규표현식의-기초와-활용
テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol
>>> p = re.compile(".+(?=:)") # 조건에선 찾되, 결과에선 빠지게 하고 싶을때. (?=:)는 :를 제외 한다는말
>>> m = p.search("http://google.com")
>>> print(m.group())
http
import re
p = re.compile(".*[.](?!bat$|exe$).*$",re.M)
l = p.findall("""
autoexec.exe
autoexec.bat
autoexec.jpg
""")
print(l)
正規式に合わせた部分を他の文字に簡単に変えることができます.
>>> p = re.compile('(blue|white|red)') # 정규식
>>> p.sub('colour', 'blue socks and red shoes') # 위 정규식과 2번째 인자가 일치하는 부분을 1인자로 바꾼다
'colour socks and colour shoes'
📌 Greedy vs Non-Greedy import re
>>> s = '<html><head><title>Title</title>'
>>> len(s)
32
>>> print(re.match('<.*>', s).span()) # Greedy '<html><head><title>Title</title>' 최대에서 가장 첫번째<>안의 문자를 가져옴
(0, 32)
>>> print(re.match('<.*>', s).group()) # Non-Greedy '<html><head><title>Title</title>'<>가 최소로 가장 첫번째 문자를 가져옴
<html><head><title>Title</title>
Reference
この問題について([ジャンプ2 Python]Chapter 08:正規表現は?Python正規表現の基礎と応用), 我々は、より多くの情報をここで見つけました
https://velog.io/@dndb3599/점프-투-파이썬-Chapter-08-정규표현식이란-파이썬-정규표현식의-기초와-활용
テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol
import re
>>> s = '<html><head><title>Title</title>'
>>> len(s)
32
>>> print(re.match('<.*>', s).span()) # Greedy '<html><head><title>Title</title>' 최대에서 가장 첫번째<>안의 문자를 가져옴
(0, 32)
>>> print(re.match('<.*>', s).group()) # Non-Greedy '<html><head><title>Title</title>'<>가 최소로 가장 첫번째 문자를 가져옴
<html><head><title>Title</title>
Reference
この問題について([ジャンプ2 Python]Chapter 08:正規表現は?Python正規表現の基礎と応用), 我々は、より多くの情報をここで見つけました https://velog.io/@dndb3599/점프-투-파이썬-Chapter-08-정규표현식이란-파이썬-정규표현식의-기초와-활용テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol