Database - 3

python에서 mariaDB Database 활용하기

  • mysql 관련 모듈 설치

    1
    $ pip install mysql-connector
  • python 소스 작성

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    from mysql.connector import MySQLConnection

    # mariaDB의 user 계정 사용하여 연결
    conn = MySQLConnection(host='localhost', database='mydb', user='jinwook', password='0000')

    # Cursor 생성

    cursor = conn.cursor()

    # mariaDB에서 사용한 SQL문 실행

    # 가독성을 위해 """ """의 멀티 라인문 사용

    cursor.execute("""

    SELECT * FROM students

    """)

    # 한번 호출에 하나의 Row data만 가져옴
    cursor.fetchone()

    # fetchmany(n)는 n개 만큼의 Row data를 한꺼번에 가져올 때 사용
    cursor.fetchmany(4)

    # 모든 data를 한꺼번에 가져올 때 사용
    cursor.fetchall()

    # 연결 종료
    cursor.close()
    conn.close()
  • 사용자 정보를 저장한 파일 사용

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    # 원하는 경로에 파일을 만든 뒤, conf.ini 파일을 만들어 사용자 정보 저장해둔 후 사용
    # mkdir today
    # cd today
    # touch conf.ini
    # vi conf.ini

    # conf.ini 내용
    """
    [mysql]
    host = localhost
    database = mydb
    user = jinwook
    password = 0000
    """

    # 같은 경로에서 다음과 같은 소스 파일 생성
    # ConfigParser module을 사용하여 외부의 설정 정보를 가져오기

    from mysql.connector import MySQLConnection
    from configparser import ConfigParser

    parser = ConfigParser()

    # 설정 파일 읽기
    # 읽었다면 출력에 ['conf.ini']로 출력
    parser.read('conf.ini')

    # 섹션 존재 확인 - 'mysql'이라는 문자열이 설정 파일 안에 존재하는지
    # boolean으로 반환
    parser.has_section('mysql')

    # 외부 설정 정보를 딕셔너리에 담기 위해 빈 딕셔너리 선언
    conf = {}

    # 섹션이 존재한다면 items(key, value)을 가져옴
    if parser.has_section('mysql'):
    items = parser.items('mysql')
    for key, value in items:
    conf[key] = value

    # conf = {'host': 'localhost', 'database': 'mydb', 'user': 'jinwook', 'password': '0000'}

    # unpacking하여 넣어줌으로써 value 값들이 들어감
    # *를 쓰면 key 값이, **를 쓰면 value 값이 들어감
    conn = MySQLConnection(**conf) # UNPACKING 사용

    # cursor 생성
    cursor = conn.cursor()

    # MariaDB 접속 후 TRANSACTION 상태 보기
    # SHOW CREATE TABLE 및 SHOW TABLE로 상태 확인

    # ct라는 테이블 생성
    # column은 id, name, score, birthday로 구성
    # CREATE 명령어는 DDL이기 때문에 사용하면 자동 commit으로 외부 세션에서 변경 확인 가능
    cursor.execute("""
    CREATE TABLE ct (
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(20) DEFAULT 'john doe',
    score SMALLINT NULL,
    birthday DATE NOT NULL);
    """)

    # INSERT 명령어는 DML이기 때문에 commit을 하지 않으면
    # 외부 세션에서는 변경한 데이터 확인 불가능
    cursor.execute("""
    INSERT INTO ct
    (name, score, birthday)
    VALUES
    ('Greg', 99, '2002-2-2');
    """)

    cursor.execute("""
    SELECT * FROM ct;
    """)

    cursor.fetchone()

    conn.commit() # DML 명령어에 대한 TRANSACTION 상태 종료(변경한 데이터 저장)

    # UDDATE는 DML 명령어
    cursor.execute("""
    UPDATE ct
    SET Score = 120
    WHERE name LIKE 'Greg';
    """)

    cursor.execute("""
    SELECT * FROM ct;""")
    cursor.fetchall()

    conn.rollback() # DML 명령어에 대한 TRANSACTION 상태 종료(변경 취소)

    cursor.execute("SELECT * FROM ct;")
    cursor.fetchone()

    # IF EXISTS 문을 사용하여 해당 데이터베이스에 테이블이 존재하지 않을 경우
    # 발생하는 에러 방지
    cursor.execute("""
    DROP TABLE IF EXISTS ct;""")

    # 연결 종료
    cursor.close()
    conn.close()

정규식(Regular Expression)

  • 정규 표현식

  • 복잡한 문자열을 처리할 때 사용하는 기법

    1
    2
    3
    4
    # 일반 문자열 메서드 사용 예시
    str = "I am your father!"
    str.startswith('I') # 특정 문자열로 시작하는지 확인하는 메서드
    >> True
    • 문자열 안에 a,b,c 중 한개의 문자와 매치되는지 확인

    • match 문은 문자열의 처음부터 매치되는지 확인

    • 매치가 된다면 match 객체를 반환

      1
      2
      3
      4
      import re # 정규식 모듈

      re.match(r'[abc]', 'abcde')
      >> <re.Match object; span=(0, 1), match='a'>
    • match 문은 문자열의 처음부터 매치되는지 확인하기 때문에 ‘345ab’는 매치되지 않음

    • 매치되지 않을 경우, None 반환

      1
      2
      print(re.match(r'[abc]', '345ab'))
      >> None
    • [a-z] : a ~ z 까지 중에 하나

      1
      2
      re.match(r'[a-z]', 'opinion')
      >> <re.Match object; span=(0, 1), match='o'>
    • [0-9] : 0 ~ 9 까지 중에 하나

      1
      2
      re.match(r'[0-9]', '345ab')
      >> <re.Match object; span=(0, 1), match='3'>
    • search 문은 문자열 전체를 검색하여 매치되는지 확인

      1
      2
      re.search(r'[abc]', '345ab')
      >> <re.Match object; span=(3, 4), match='a'>
    • ‘+’는 최소 한 번 이상 반복될 때 매치가 된다고 판단한다.(1 ~ 무한대)

    • 0번 반복될 경우 매치되지 않는다고 판단한다.

      1
      2
      re.search(r'[abc]+', 'abc123')
      >> <re.Match object; span=(0, 3), match='abc'>
    • 즉, 일치되는 결과가 하나도 없을 경우 매치되지 않는다고 판단

      1
      2
      print(re.search(r'[abc]+', '123'))
      >> None
    • ‘*’는 최소 0번 이상 반복될 때 매치가 된다고 판단한다.(0 ~ 무한대)

    • 즉, 일치되는 결과가 없더라도 매치된다고 판단

      1
      2
      re.search(r'[abc]*', '123')
      >> <re.Match object; span=(0, 0), match=''>
    • ‘?’는 해당 문자가 있어도 되고 없어도 된다는 의미

      1
      2
      re.search(r'[abc]?', '123')
      >> <re.Match object; span=(0, 0), match=''>
    • {n}은 반드시 n개의 개수를 가지고 있는지 판단

    • 일치하지 않을 경우 매치되지 않는다고 판단

      1
      2
      print(re.search(r'a{4}', 'aabb'))
      >> None
    • 매치된 경우

      1
      2
      re.search(r'a{4}', 'aaaaaaaab')
      >> <re.Match object; span=(0, 4), match='aaaa'>
    • {m,n}은 m ~ n개 반복되는지 판단

    • 선언할 때 {3, 10}처럼 ‘3,’ 다음에 공백을 두면 매치가 되지 않으므로 주의

    • 공백을 두지 말자

      1
      2
      re.search(r'a{3,10}', 'aaaaaabbbbb')
      >> <re.Match object; span=(0, 6), match='aaaaaa'>
    • abc가 있는지 전체 문자열에서 판단

      1
      2
      re.search(r'abc', 'defabcxyz')
      >> <re.Match object; span=(3, 6), match='abc'>
    • ‘^’는 문자열의 처음이 abc인지 판단

    • 처음이 항상 abc라면 매치되고 그렇지 않으면 매치되지 않음

      1
      2
      print(re.search(r'^abc', 'defabcxyz'))
      >> None
    • ‘^’가 만약 문자 클래스인 [] 안에 들어간다면 처음을 의미하는 것이 아님

    • 반대(not)의 의미를 가짐

    • [^abc]인 경우, a, b, c가 아닌 문자만 매치

      1
      2
      re.search(r'[^abc]', 'abbbababacczabbaba')
      >> <re.Match object; span=(11, 12), match='z'>
    • ‘$’는 문자열의 마지막이 xyz인지 판단

    • 마지막이 항상 xyz라면 매치되고 그렇지 않으면 매치되지 않음

      1
      2
      print(re.search(r'xyz$', 'xyzabc'))
      >> None
    • 매치된 경우

      1
      2
      re.search(r'xyz$', 'abcxyz')
      >> <re.Match object; span=(3, 6), match='xyz'>
    • 자주 사용하는 정규 표현식은 별도의 표현법으로 표현 가능

    • 대문자는 소문자의 반대인 경우를 의미

      • \d = [0-9]

      • \D = [^0-9]

      • \s = whitespace 문자 = [ \t\n\r\f\v](맨 앞 공백도 의미)

      • \S = whitespace가 아닌 문자 = [^ \t\n\r\f\v]

      • \w = [a-zA-Z0-9_]

      • \W = [^a-zA-Z0-9_]

    • 0부터 9까지 하나라도 반복될 경우 매치

      1
      2
      re.search(r'\d+', 'abc 123 bbb')
      >> <re.Match object; span=(4, 7), match='123'>
    • match 객체의 메서드를 이용

      1
      2
      3
      m = re.search(r'\d+', 'abc 123 bbb')
      m
      >> <re.Match object; span=(4, 7), match='123'>
    • group() : 매치된 문자열 리턴

      1
      2
      m.group()
      >> '123'
    • start() : 매치된 문자열의 시작 위치 리턴

      1
      2
      m.start()
      >> 4
    • end() : 매치된 문자열의 끝 위치 리턴

      1
      2
      m.end()
      >> 7
    • span() : 매치된 문자열의 (시작, 끝)에 해당되는 튜플 리턴

      1
      2
      m.span()
      >> (4, 7)
    • 전화번호 매치

    • 다양하게 표현된 문자열의 전화번호를 매치

    • 첫번째 번호 자리를 매치시킬 수 있는 \d{3} = [0-9]{3} = 0 ~ 9 중에 반드시 3자리 매치

    • 번호 사이에 올 수 있는 문자들의 반복(*)까지 추가 = [-./ ]*

    • 가운데 번호 자리는 3 ~ 4자리이므로 \d{3,4}

    • 마지막 번호 자리를 매치시킬 수 있는 \d{4}

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      cell_li = ['010-1111-2222', '01022223333', '010.333.4444', '010 4444 5555']
      pattern = r'\d{3}[-./ ]*\d{3,4}[-./ ]*\d{4}'

      for cell in cell_li:
      m = re.search(pattern, cell)
      if m:
      print(m.group())

      >> 010-1111-2222
      01022223333
      010.333.4444
      010 4444 5555
    • Dot(.)는 모든 문자를 표현할 수 있기 때문에 모든 문자와 매칭됨

      1
      2
      re.search(r'a.z', 'abz')
      >> <re.Match object; span=(0, 3), match='abz'>
    • Dot 두개는 문자 두개를 의미

      1
      2
      re.search(r'a..z', 'ahyz')
      >> <re.Match object; span=(0, 4), match='ahyz'>
    • 백슬래시() 문자를 찾을 때…

    • ‘\‘ 문자가 문자열 그 자체임을 알려주기 위해 백슬래시 2개를 사용하여 처리해야 한다.

    • 하지만 파이썬 문자열 리터럴 규칙에 의해 ‘\\‘로 명시하여도 다시 ‘\‘로 변경하여 전달한다.

    • 따라서 백슬래시 4개(‘\\\\‘)를 사용해야 한다.

    • 파이썬에서 사용할 때만 적용되는 문제

      1
      2
      re.search('\\\\', 'dir1\dir2')
      >> <re.Match object; span=(4, 5), match='\\'>
    • 너무 복잡하기 때문에 파이썬 정규식에는 Raw string이라는 것이 존재

    • r을 붙여줌

      1
      2
      re.search(r'\\', 'dir1\dir2')
      >> <re.Match object; span=(4, 5), match='\\'>
    • 그룹핑(Grouping)

    • 매치된 문자열 중에서 반복되는 문자열을 찾거나, 특정 부분의 문자열을 출력하기 위해 사용

    • 해당 부분에 ‘()’로 묶어주면 된다.

      1
      2
      3
      4
      5
      pattern = r'(\d{3})[-./ ]*(\d{3,4})[-./ ]*(\d{4})'
      string = '010-1111-2222'
      m = re.search(pattern, string)
      m
      >> <re.Match object; span=(0, 13), match='010-1111-2222'>
    • 매치된 전체 문자열 리턴

      1
      2
      m.group()
      >> '010-1111-2222'
    • 첫번째 그룹에 해당되는 문자열 리턴

      1
      2
      m.group(1)
      >> '010'
    • 두번째 그룹에 해당되는 문자열 리턴

      1
      2
      m.group(2)
      >> '1111'
    • 세번째 그룹에 해당되는 문자열 리턴

      1
      2
      m.group(3)
      >> '2222'
    • 그룹 범위가 벗어난 경우 에러

      1
      2
      m.group(4)
      >> IndexError: no such group
    • 그룹핑의 범위는 문자열이 시작된 처음을 1번 그룹으로 한다.

    • ‘(a(b)c)’인 경우, abc 앞에 있는 ‘()’가 처음이고 그 다음이 b에 있는 ‘()’가 된다.

      1
      2
      3
      4
      5
      6
      7
      8
      pattern = r'(a(b)c)'
      string = 'abcde'
      m = re.search(pattern, string)
      m
      >> <re.Match object; span=(0, 3), match='abc'>

      m.group()
      >> 'abc'
    • 첫번째 그룹 문자열 리턴

      1
      2
      m.group(1)
      >> 'abc'
    • 두번째 그룹 문자열 리턴

      1
      2
      m.group(2)
      >> 'b'
    • 그룹 범위가 벗어난 경우 에러

      1
      2
      m.group(3)
      >> IndexError: no such group
    • compile 옵션

    • compile 문을 활용하는 경우, 패턴 객체를 만들고 여러번 재사용이 가능

    • 반복적인 매칭 작업이 필요한 경우에는 패던을 미리 컴파일해서 시간 단축

      1
      2
      3
      p = re.compile(pattern)
      p.match(string)
      >> <re.Match object; span=(0, 3), match='abc'>
    • ‘a = 100’이라는 문자열을 group으로 나눴을 때 각각 a, =, 100 출력하기

    • 변수 처음은 문자가 올 수 있지만, 숫자는 올 수 없다.(ex) 1a는 안됨)

    • [a-zA-Z_]로 처음 오는 문자가 숫자가 될 수 없도록 함

    • \w*으로 다음 오는 문자가 무엇이든 반복이 얼마가 되든 상관없게 선언

    • 사이에 공백으로 인해 \s*

    • 할당하는 숫자는 반드시 하나는 와야 하므로 ‘+’를 사용하여 [0-9]+ 선언

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      pattern = r'([a-zA-Z_]\w*)\s*=\s*([0-9]+)'
      p = re.compile(pattern)
      m = p.search('a_1 = 100')
      m.group()
      >> 'a_1 = 100'

      m.group(1)
      >> 'a_1'

      m.group(2)
      >> '100'
Share