Category Archives: Programming

러스트에서 `String`을 이용할 때, 생기는 유니코드 문제점

러스트에서 문자열을 다루는 방법은 String을 이용하는 것입니다. 그런데 이것을 이용하더라도 유니코드를 사용하는 경우 인텍싱이 다른 언어와 같이 명확하게 처리되지 않습니다. 다음 코드를 살펴봅시다.

fn main() {
let s1 = String::from("hello world!");
let hello = &s1[0..5];
let world = &s1[6..11];
println!("{}, {}", hello, world);

let s1 = String::from("안녕 세계!");
let hello = &s1[0..6];
let world = &s1[7..13];
println!("{}, {}", hello, world);

let s1 = String::from("こんにちは世界!");
let hello = &s1[0..15];
let world = &s1[15..21];

println!("{}, {}", hello, world);
}

윗 코드를 실행하면 다음과 같은 결과 나옵니다. 앞에서 인텍싱하는 코드를 살펴보면 글자 수와 인덱싱 숫자가 맞지 않는 것을 알 수 있습니다.

❯ cargo run
Compiling ownership v0.1.0 (/Users/jaehwan/git/rust/projects/ownership)
Finished dev [unoptimized + debuginfo] target(s) in 0.39s
Running `target/debug/ownership`
hello, world
안녕, 세계
こんにちは, 世界

참고하세요!

엔서블에서 apt 이용할 때 나오는 오류 처리법

apt을 이용해 업그레이드할 때 통상적으로 다음과 같이 하는 경우가 많습니다.

tasks:
- name: Update and upgrade apt packages
become: true
apt:
upgrade: yes
update_cache: yes
cache_valid_time: 86400 #One day

이런 경우 다음과 같은 경고가 뜰 수 있죠.

TASK [Update and upgrade apt packages] ************************************************************************************************************************
[WARNING]: The value "True" (type bool) was converted to "'True'" (type string). If this does not look like what you expect, quote the entire value to ensure
it does not change.

이건 upgrade: yesupgrade: "yes"과 같이 바꾸면 해결됩니다. 즉 아래와 같이 하면 됩니다.

tasks:
- name: Update and upgrade apt packages
become: true
apt:
upgrade: "yes" # not: yes
update_cache: yes
cache_valid_time: 86400 #One day

참고: ubuntu – Ansible warning about boolean type conversion – Stack Overflow

파이썬으로 Docker로 구성한 MariaDB 사용하기

도커를 이용하여 MariaDB(이하 마리아디비)를 설치해봤습니다. 이렇게 설치한 마리아디비에 파이썬으로 이용하여 자료를 저장하고 저장한 값을 확인하고 지워보겠습니다. 우선 파이썬으로 마리아디비와 같은 실제 DB(이하 데이터베이스)에 접근하기 위해서는 해당 데이터베이스에 맞는 드라이버(또는 클라이언트 라이브러리(client library)라고 하기도 합니다)를 설치해줘야 합니다. 이 글에서는 PyMySQL을 사용하겠습니다. 설치 방법은 Installation을 참고하세요. 현재 사용하고 있는 PyMySQL의 버전은 0.10.1입니다.

본격적으로 시작하기에 앞서

참고로 여기에서는 이미 Docker로 MariaDB를 설치했다고 가정할 것입니다. 설치하는 방법은 다음 링크를 참고하세요. Docker를 사용해서 MariaDB을 설치하기 그리고 이미 다음과 같은 sql명령을 사용하여 gregs_list이라는 DATABASE를 만들고, 그 안에 my_contacts라는 테이블을 만들고 두 사람의 자료를 입력했습니다.그리고 사용하기 쉽게 하기 위해서 sqlalchemy을 사용합니다. 이것의 설치 방법은 How to Install SQLAlchemy on Windows, Mac and Linux | Python Central을 참고하세요!

CREATE DATABASE gregs_list;

CREATE TABLE my_contacts(
last_name VARCHAR(30),
first_name VARCHAR(20),
email VARCHAR(50),
birthday Date,
profession VARCHAR(50),
location VARCHAR(50),
status VARCHAR(20),
interests VARCHAR(100),
seeking VARCHAR(100)
);

INSERT INTO my_contacts(
last_name, first_name, email, gender, birthday, profession, location, status, interests, seeking
)
VALUES (
'Anderson', 'Jillian', 'jill_anderson@breakneckpizza.com', 'F', '1980-09-05', 'Technical Writer', 'Palo Alto, CA', 'Single', 'Kayaking, Reptiles', 'Relationship, Friends'
);

INSERT INTO my_contacts(
first_name, email,  profession, location
)
VALUES (
'Pat', 'patpost@breakneckpizza.com', 'Postal Worker', 'Princeton, NJ'
);

위에서 작업한 결과로 gregs_list 데이터베이스의 my_contacts라는 테이블에는 다음과 같이 두 사람의 자료가 들어있습니다.

MariaDB [gregs_list]> SELECT * FROM my_contacts;
+-----------+------------+----------------------------------+--------+------------+------------------+---------------+--------+--------------------+-----------------------+
| last_name | first_name | email                            | gender | birthday   | profession       | location      | status | interests          | seeking               |
+-----------+------------+----------------------------------+--------+------------+------------------+---------------+--------+--------------------+-----------------------+
| Anderson  | Jillian    | jill_anderson@breakneckpizza.com | F      | 1980-09-05 | Technical Writer | Palo Alto, CA | Single | Kayaking, Reptiles | Relationship, Friends |
| NULL      | Pat        | patpost@breakneckpizza.com       | NULL   | NULL       | Postal Worker    | Princeton, NJ | NULL   | NULL               | NULL                  |
+-----------+------------+----------------------------------+--------+------------+------------------+---------------+--------+--------------------+-----------------------+
2 rows in set (0.004 sec)

데이터베이스에 입력된 것을 가져오기

그러면 파이썬으로 이것에 연결해보겠습니다. 도커에 설치된 마리아디비의 정보를 파이썬에 입력하기 위해서 정보가 들어 있는 dict를 하나 만들었습니다.

db = {
'user'     : 'root',
'password' : 'RT27hDosK',
'host'     : '0.0.0.0',
'port'     : '3306',
'database' : 'gregs_list'
}

위에 입력한 자료를 가지고 DB_URL이라는 문자열을 만들어서 이를 가지고 데이터 베이스에 접근하기 위해 sqlalchemy으로 create_engine을 만들고, my_contacts 테이블에 접근하기 위해 execute을 가지고 sql을 직접 실행합니다. 그렇게 받아온 자료를 print를 이용해 확인합니다.

import sqlalchemy as sa
DB_URL = f"mysql+pymysql://{db['user']}:{db['password']}@{db['host']}:{db['port']}/{db['database']}?charset=utf8"
conn = sa.create_engine(DB_URL, encoding = 'utf-8')

rows = conn.execute('SELECT * FROM my_contacts')

for row in rows:
print(row)

지금까지 한 것을 실행하면 다음과 같습니다. 맥에서 실행한 것이니 조금씩 다를 수는 있겠지만, 거의 다 같게 작동할 것입니다. 만약 자료를 보기 위해서 print(rows)라고 하신다면 <sqlalchemy.engine.result.resultproxy object="" at="" 0x102e33940="">이라고 나올 것입니다. 왜냐하면 sqlalchemy의 ResultProxy 객체는 출력해서 볼 수 없기 때문입니다.</sqlalchemy.engine.result.resultproxy>

❯ python3
Python 3.8.6 (default, Oct 16 2020, 14:50:59)
[Clang 11.0.3 (clang-1103.0.32.62)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sqlalchemy as sa
>>> db = {
...     'user'     : 'root',
...     'password' : 'RT27hDosK',
...     'host'     : '0.0.0.0',
...     'port'     : '3306',
...     'database' : 'gregs_list'
... }
>>> DB_URL = f"mysql+pymysql://{db['user']}:{db['password']}@{db['host']}:{db['port']}/{db['database']}?charset=utf8"
>>> conn = sa.create_engine(DB_URL, encoding = 'utf-8')
>>> rows = conn.execute('SELECT * FROM my_contacts')
>>> for row in rows:
...     print(row)
...
('Anderson', 'Jillian', 'jill_anderson@breakneckpizza.com', 'F', datetime.date(1980, 9, 5), 'Technical Writer', 'Palo Alto, CA', 'Single', 'Kayaking, Reptiles', 'Relationship, Friends')
(None, 'Pat', 'patpost@breakneckpizza.com', None, None, 'Postal Worker', 'Princeton, NJ', None, None, None)

위의 방법을 보시면 아시겠지만, 기본적으로 SQL을 이용하는 것과 별 차이가 없습니다. 또한 PostgreSQL와 같은 다른 종류의 데이터베이스를 사용하면, 사용할 수 있는 SQL가 다르기 때문에 각각 DB에 맞춰 코드를 짜야 합니다. 이처럼 같은 기능을 하지만, 각각의 데이터베이스에서는 상이한 표현으로 되어 있는 것들을 같은 함수로 처리할 수 있는 기능이 SQLAlchemy에 들어 있습니다. 이 기능이 바로 ‘SQL Expression Language’입니다. 이것을 이용하여 위의 것과 동일한 일을 하는 코드를 작성하면 아래와 같습니다.

db = {
'user'     : 'root',
'password' : 'RT27hDosK',
'host'     : '0.0.0.0',
'port'     : '3306',
'database' : 'gregs_list'
}
import sqlalchemy as sa

DB_URL = f"mysql+pymysql://{db['user']}:{db['password']}@{db['host']}:{db['port']}/{db['database']}?charset=utf8"

conn = sa.create_engine(DB_URL, encoding = 'utf-8')

meta = sa.MetaData()

my_contacts = sa.Table('my_contacts', meta,
sa.Column('last_name', sa.String),
sa.Column('first_name', sa.String),
sa.Column('email', sa.String),
sa.Column('birthday', sa.Date),
sa.Column('profession', sa.String),
sa.Column('location', sa.String),
sa.Column('status', sa.String),
sa.Column('interests', sa.String),
sa.Column('seeking', sa.String))

meta.create_all(conn)

result = conn.execute(my_contacts.select())
rows = result.fetchall()
print(rows)

이 코드를 보시면 아시겠지만, SQL에서 벗어나, 순수 파이썬 코드처럼 보입니다. 윗 코드를 실행시키면 앞에 한 것과 동일한 결과가 나옵니다. 앞부분은 윗 코드와 동일하지만 데이블에 접근하는 방법은 많이 다릅니다.

Python 3.8.6 (default, Oct 16 2020, 14:50:59)
[Clang 11.0.3 (clang-1103.0.32.62)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sqlalchemy as sa
>>> db = {
...     'user'     : 'root',
...     'password' : 'RT27hDosK',
...     'host'     : '0.0.0.0',
...     'port'     : '3306',
...     'database' : 'gregs_list'
... }
>>> DB_URL = f"mysql+pymysql://{db['user']}:{db['password']}@{db['host']}:{db['port']}/{db['database']}?charset=utf8"
>>> conn = sa.create_engine(DB_URL, encoding = 'utf-8')
>>> meta = sa.MetaData()
>>> my_contacts = sa.Table('my_contacts', meta,
...     sa.Column('last_name', sa.String),
...     sa.Column('first_name', sa.String),
...     sa.Column('email', sa.String),
...     sa.Column('birthday', sa.Date),
...     sa.Column('profession', sa.String),
...     sa.Column('location', sa.String),
...     sa.Column('status', sa.String),
...     sa.Column('interests', sa.String),
...     sa.Column('seeking', sa.String))
>>> meta.create_all(conn)
>>> result = conn.execute(my_contacts.select())
>>> rows = result.fetchall()
>>> print(rows)
[('Anderson', 'Jillian', 'jill_anderson@breakneckpizza.com', datetime.date(1980, 9, 5), 'Technical Writer', 'Palo Alto, CA', 'Single', 'Kayaking, Reptiles', 'Relationship, Friends'), (None, 'Pat', 'patpost@breakneckpizza.com', None, 'Postal Worker', 'Princeton, NJ', None, None, None)]

이것을 이용해서 자료를 입력해보겠습니다. 참고로 생년월일을 입력하기 위해서 datetime가 필요합니다.

import datetime

conn.execute(my_contacts.insert(('Soukup', 'Alan', 'soupup@breakneckbizza.com', datetime.date(1975, 12, 2), 'Aeronautical Engineer', 'San Antonio, TX', 'Married', 'RPG, Programming', 'Nothing')))

윗 코드를 작동시켜보면 자료가 입력된 것을 확인할 수 있습니다.

>>> import datetime
>>> conn.execute(my_contacts.insert(('Soukup', 'Alan', 'soupup@breakneckbizza.com', datetime.date(1975, 12, 2), 'Aeronautical Engineer', 'San Antonio, TX', 'Married', 'RPG, Programming', 'Nothing')))
>>> result = conn.execute(my_contacts.select())
>>> rows = result.fetchall()
>>> print(rows)
[('Anderson', 'Jillian', 'jill_anderson@breakneckpizza.com', datetime.date(1980, 9, 5), 'Technical Writer', 'Palo Alto, CA', 'Single', 'Kayaking, Reptiles', 'Relationship, Friends'), (None, 'Pat', 'patpost@breakneckpizza.com', None, 'Postal Worker', 'Princeton, NJ', None, None, None), ('Soukup', 'Alan', 'soupup@breakneckbizza.com', datetime.date(1975, 12, 2), 'Aeronautical Engineer', 'San Antonio, TX', 'Married', 'RPG, Programming', 'Nothing')]

Array 에서 행을 잡는 방법

줄리아에서 아래와 같은 array를 가지고 for 문을 돌려 열을 선택하고 싶은 경우가 있다.

M = [1 2 3; 4 5 6; 7 8 9]
3×3 Array{Int64,2}:
1  2  3
4  5  6
7  8  9

그러나 아쉽게도 다음과 같이 셀을 하나씩 잡아서 준다.

for i in (M)
println(i)
end
1
4
7
2
5
8
3
6
9

이때 필요한 eachrow()함수이다. 이를 이용해서 for 문을 돌리면 아주 깔금하게 작동합니다.

for row in eachrow(M)
println(row)
end
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]

라즈베리 파이에 Python 3.7.2 설치

라즈베리 파이에는 Python 3.5.3이 설치되어 있어서, 여기에 Python 3.7.2을 설치하려고 합니다.

아래 코드의 출처 다음과 같습니다.

Installing Python 3.7.2

라즈베리 파이의 터미널에서 다음과 같이 입력하시면 설치가 됩니다.

sudo apt install build-essential checkinstall
sudo apt install libreadline-gplv2-dev libncursesw5-dev libssl-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev
sudo apt-get install libffi-dev
cd /usr/src
sudo wget https://www.python.org/ftp/python/3.7.2/Python-3.7.2.tgz
sudo tar xzf Python-3.7.2.tgz
cd Python-3.7.2
sudo ./configure --enable-optimizations
sudo make altinstall

아마도 다음과 같은 문구가 나온다면 설치가 완료된 것입니다.

Installing collected packages: setuptools, pip
Successfully installed pip-18.1 setuptools-40.6.2

Python-3.7.2를 실행하시려면 터미널에서 python3.7이라고 입력하시면 됩니다.

ps: 2019년 4월 16일 현재 Python-3.7.3이 나왔습니다. 이것을 설치하시려면, 위의 코드에서 3.7.23.7.2으로 변경하시면 됩니다.

파이썬에서 함수를 만들 때, 리턴값으로 namedtuple을 써보자.

파이썬에서 리턴값이 조금 복잡해지면, 딕셔너리를 많이 사용했었습니다. 그런데 이렇게 만든 함수를 쓰려면(함수 이름을 ‘fun’이라고 합시다!), fun[‘name’] 과 같이 딕셔너리를 불러오는 방식으로 써야합니다. 뭐 키보드를 많이 두드려야 합니다. 그래서 이를 쉽게 하는 방법을 찾다가 Not using named tuples when returning more than one value from a function이라는 글을 봤습니다. namedtuple을 이용해서 하면 fun.name과 같이 쉽게 처리할 수 있습니다. 윗글에서 코드를 가져왔습니다. 아주 쉽게 처리할 수 있습니다! name = namedtuple("name", ["first", "middle", "last"]), 코드에서 namedtuple을 만들어서 return name("Richard", "Xavier", "Jones")같이 사용하는 것입니다. 그러면 만들 때 사용했던, ["first", "middle", "last"]을 가지고 처리할 수 있게 됩니다.

from collections import namedtuple

def get_name():
    name = namedtuple("name", ["first", "middle", "last"])
    return name("Richard", "Xavier", "Jones")

name = get_name()

# much easier to read
print(name.first, name.middle, name.last)

내일 날자를 만들어 봅시다.

swift에서 초 단위의 데이터를 사용하지 않고, 내일 날자를 구하는 코드입니다.

// 아래 코드는 swift 2.0에서 현재 시간에 하루를 더하는 것을 보여주고 있습니다.
let now = NSDate() // 현재 시간 정보를 넣습니다.
let comp = NSDateComponents() // 하루를 더하기 위해서 NSDateComponents를 하나 만듭니다.
comp.setValue(1, forKey: "day") // 위에서 만든 곳에 1일을 넣습니다. forKey를 바꾸면 다른 것도 넣을 수 있습니다.
let myCal = NSCalendar.init(calendarIdentifier: NSCalendarIdentifierGregorian) // 하루를 더해서 넣을 NSCalendar를 하나 만듭니다.
let tomorrow = myCal!.dateByAddingComponents(comp, toDate: now, options: NSCalendarOptions(rawValue: 0)) // 위에서 만든 현재 시간인 now에 myCal을 이용하여 하루를 더해서 tomorrow에 넣습니다.

view raw

AddOneDay.swift

hosted with ❤ by GitHub

String 를 NSData 로, NSData 를 String 로

swift에서 String를 NSData로, NSData를 String로 바꾸는 코드입니다.

var swift_string = "lorem ipsum dolor sit amet"
// from String to NSData
let data = swift_string.dataUsingEncoding(NSUTF8StringEncoding)
print(data)
// from NSData to String
var out: String = String(data:data!, encoding:NSUTF8StringEncoding)!
print(out) // print "lorem ipsum dolor sit amet"