람다 표현식(Lambda Expression)

람다 표현식은 JAVA SE 8부터 추가된 기능입니다. 람다 표현식은 함수를 하나의 식으로 표현한 것인데요. 메소드의 이름을 갖다버린 익명 함수(Anonymous Function)로 볼 수 있습니다.

아주 간단히 이야기하자면 귀찮은 익명 메소드(자바에서는 익명 클래스, Anonymous Class)를 아주 간단하게 줄인 표현식이라고 생각하면 됩니다.

 

기존 방식

예를 들어 안드로이드에서 클릭 이벤트 발생시에 아래와 같이 View.OnClickListener를 정의해주고 onClick 메소드를 채워줘야 클릭 이벤트를 받을 수 있습니다.

button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e(TAG,"클릭 발생");
            }
        });

 

구현해야할 메소드는 onClick 메소드 하나 뿐인것을 알 수 있습니다. onClick 메소드는 너무나 간단하게 로그를 찍는 코드밖에 없습니다. 하지만 꽤나 복잡한 과정을 거치게 되죠. 분명 로그만 찍는데 부가적으로 기록해야하는 코드가 길다는 것이죠. 

혹은 우리가 자주 사용하는 Thread를 Runnable을 통해서 객체 생성과 동시에 동작시키려면 이렇게 코드를 짜야합니다.

new Thread(new Runnable() {
    		@Override
    		public void run() {
    			System.out.println("Thread run");
    		}
    	}).start();

 

 

람다를 사용한 방식

만약 위의 코드들에 람다표현식을 적용한다면 이렇게 바뀝니다.

button.setOnClickListener(v->Log.e(TAG,"클릭 발생"));

 

그리고 Thread를 동작시키는 코드는 아래와 같게됩니다.

new Thread(()->System.out.println("Thread run")).start();

 

몇줄이 단지 한줄로 바뀌게 되지요. 이렇게 람다를 적용하면 이렇게 간단한 코드로 바뀌게 됩니다. 즉, 가독성이 정말 높아지게 됩니다. 여기서 우리는 단지 하나의 익명 클래스를 구현했지만, 그것이 여러개가 된다면 가독성이 매우 떨어지게 됩니다.

 

반면 익명 함수이기 때문에 재사용이 불가능하고, 디버깅이 어려운 단점이 있습니다. 

 

람다 표현식 문법

이제는 람다를 사용하는 방법에는 어떤 방법이 있는지 알아보도록 합시다. 이때 람다를 사용할 수 있는 경우는 Overriding해야할 메소드가 하나여야합니다. 두개 이상의 메소드를 구현해야할때에는 사용할 수가 없습니다.

1. 구현부가 한줄인 경우

() -> System.out.println(x)

 

2. 인자가 있는 경우

() -> {System.out.println("lambda")};

 

3. 인자가 있는 경우

인자가 2개 이상인 경우에는 괄호로 묶어야합니다. 그리고 타입을 명시적으로 지정해줄 수도 있습니다.

x -> {System.out.println(x)};
(x) -> {System.out.println(x)};
(x,y) -> {System.out.println(x+","+y)};
(int x, int y) -> {System.out.println(x+","+y)};

 

4. 반환값이 있을 경우

(int x) -> { return x*x };

 

 

함수형 프로그래밍

람다를 사용하는 가장 주된 이유는 함수형 프로그래밍을 해줄 수 있게 만들어주기 때문입니다. 여기서 함수형 프로그래밍이라는 것은 순수 함수, 메소드로 문제를 해결하는 프로그래밍 페러다임입니다. 자바 8 이전에는 어떻게(how) 문제를 해결하는 것에 초점을 두지만, 8 이후에는 무엇(what)을 할 것인가에 대해서도 지원하게 됩니다.

예를 들어 for을 우리가 구현하지 않고 미리 정의된 forEach를 사용하는 방식입니다. 이렇게 되면 코드를 짧게 유지하여 유지 보수하기가 매우 쉬워집니다. 

 

함수형 프로그래밍을 도와주는 몇몇 기법에는 지금 소개한 람다 표현식과 StreamAPI 등이 있습니다.

반응형
블로그 이미지

REAKWON

와나진짜

,

 

수행 시간 구하기(clock 함수)

 

여러분이 짠 프로그램의 얼마나 효율적으로 동작하는지 궁금하다거나 회사에서 솔루션을 개발하다가 본인의 함수가 얼마나 빠르게 동작하는 지 확인해볼 필요가 있습니다. 그럴때 프로그램이 얼마나 실행됐는지 궁금하시다면 clock()함수로 구할 수가 있습니다. clock함수는 아래와 같이 정의가 되어있습니다. time.h 헤더파일을 include해야함을 알 수가 있네요.

 

#include <time.h>
clock_t clock(void);

 

이 함수에 대해서 프로그램 수행시간은 아래에서 실제 구현하긴 할 것인데, 우선 이 함수에 대해서 설명부터 하도록 하겠습니다.

clock함수는 clock_t라는 값을 반환하는데 이 값은 CPU가 사용된 값을 나타냅니다. 정확히는 시간이 아닌 clock수를 반환하지요. 초 단위로 구하려면 초당 클록수를 알아야하는데 아래의 매크로를 사용하면 됩니다.

CLOCKS_PER_SEC

이처럼 초 단위로 바꾸고 싶다면 CLOCKS_PER_SEC라는 매크로로 나누어서 초단위로 구할 수 있습니다. clock수를 어떤 이유로 얻어올 수 없다면 (clock_t) -1 이 반환됩니다.

또한 이 함수는 Thread-Safety한 특징을 갖습니다.

이제 정말 프로그램의 수행시간을 알아내는 코드를 보도록 합시다. 아래의 코드는 for루프를 수행한 수행시간을 구합니다. 1초 이내의 끝이 날 것이기 때문에 보다 밀리-세컨 단위의 더 정확한 시간을 측정하기 위해서 clock_t 자료형에서 double로 형변환을 한 것입니다.

 

#include <time.h>
#include <stdio.h>

int main(void)
{
    int  i;
    double start, end;

    //for 루프 시작 시간
    start = (double)clock() / CLOCKS_PER_SEC;    

    //for루프 100000000번 돌아보기
    int sum = 0;
    for (i = 0; i < 100000000; i++) {
        sum++;
    }

    //for 루프 끝난 시간
    end = (((double)clock()) / CLOCKS_PER_SEC);
    printf("프로그램 수행 시간 :%lf\n", (end-start));
}

 

결과 

 

수행 결과

 

제 컴퓨터가 좋은 편이 아니라 0.436초 걸리고 난 후에 프로그램이 종료되었음을 알 수 있네요. 프로그램 수행시간을 측정하는 방법은 이처럼 clock() 함수를 이용해서 구해낼 수 있습니다. 이렇게 우리가 만든 알고리즘이 얼마나 걸리는지도 확인해볼 수 있겠습니다.

 

반응형
블로그 이미지

REAKWON

와나진짜

,

문자열을 숫자로 변환하는 함수

문자열을 숫자 자료형으로 변환하려면 어떤 방법으로 변환할 수 있을까요? 만약 입력이 숫자의 문자열이라고 가정한다면 아래의 코딩으로 숫자로 변환할 수 있습니다.

#include <stdio.h>

int my_atoi(const char* str) {
	int ret = 0;
	int i;
	for (i = 0; str[i] != '\0'; i++)	//NULL문자만나면 for문 종료
		ret = ret * 10 + (str[i] - '0');	//숫자 문자에 '0'을 빼면 레알 숫자가 구해짐
	return ret;
}
int main() {
	int str = "12345";
	printf("%d\n", my_atoi(str));

}

 

문자 하나에서 '0'을 빼면 이것이 곧 숫자 0이 된다는 점을 이용한 코드이지요. 사실 이렇게 간단하게 짤 수도 있겠지만 문자열에 숫자말고도 알파벳같은 글자가 섞여 들어오면 처리가 필요하게 됩니다. 코드짤때 에러 처리하는 것은 여간 귀찮은 것이 아니지요. 위 처럼 접근하는 것은 좋긴하지만 급할때 삽질하지 말라고 아래의 함수들을 C언어 라이브러리에서 제공해줍니다.

 

지금 소개해드릴 함수들이 그런 함수들입니다. 아래의 함수들을 사용하기 위해서는 stdlib.h 헤더파일을 우선 include시켜줘야한다는 점 기억하시구요.

 

atoi(Ascii To Integer) - int로 변환

#include <stdlib.h>
int atoi(const char* str);

atoi함수는 문자열을 정수형으로 변환시켜주는 함수입니다. 함수명의 앞글자 a는 Ascii를 의미합니다. 이후의 모든 함수의 a는 ascii의 a라는 점은 알아두시면 나중에 배운척 할 수 있습니다. 이 함수가 받는 str 문자열에서 숫자가 아닌 글자들은 모두 무시됩니다.

변환할 수 없는 값이라면 0을 반환합니다.

 

atol(Ascii To Long) - long으로 변환

atoll(Ascii To Long Long) - long long int로 변환

#include <stdlib.h>
long int atol(const char *str);
long long int atoll(const char *str);

long 형태로 입력을 받고 싶다면 atol 함수를 이용하면 됩니다. 이것도 모자라 long int의 범위를 넘어서는 큰 수를 입력받고 싶을때가 있습니다. 보다 큰 범위의 수를 변환하기를 원한다면 atoll 함수를 이용하여 변환할 수 있습니다. 

반환할 수 없으면 0L을 반환합니다.

 

atof(Ascii To Float) - 부동소수점 값으로 변환

#include <stdlib.h>
double atof(const char *str);

정수형말고도 소숫점으로 변환할 수 있는 함수도 있습니다. atof라는 함수이지요. 전달받은 문자열을 부동 소수점 값으로 변환해주는 함수입니다. 여기서 float자료형을 반환하는 게 아닌 double 자료형으로 return 한다는 것을 유의하세요. 

만약 함수를 통해서 반환할 수 없다면 반환값은 0이 됩니다.

 

단순한 설명보다는 무엇보다 어떻게 사용하는지 코드를 보면서 이해를 하는게 좋습니다. 

아래의 코드는 기본적인 함수들의 사용법을 보여주는 예제입니다.

#include <stdio.h>
#include <stdlib.h>
int main() {
	char* ll_str = "987654321123456789";
	char* i_str = "1234";
	char* f_str = "1234.567";
	//long long 자료형 출력
	printf("%lld\n", atoll(ll_str));

	//정수형 10진수로 출력
	printf("%d\n", atoi(i_str));

	//부동 소수점으로 출력
	printf("%.3f\n", atof(f_str));

	i_str = "1234에 문자열 꼽사리";
	printf("%d\n", atoi(i_str));

}

 

결과 화면

 

위 결과에서도 확인할 수 있듯이 "1234"에 다른 문자열 "에 문자열 꼽사리"를 섞어도 숫자만 추출해내는 것을 알 수 있습니다. 

이처럼 C언어에서는 문자열을 숫자로 변환시켜주는 함수를 제공하니 알맞게, 필요할때 사용하시면 되겠습니다.

반응형
블로그 이미지

REAKWON

와나진짜

,

파이썬 파일 다루기(File Handling)

모든 언어에서 파일을 다루는 것은 매우 중요한 일이고 필수적으로 알아두어야합니다. 프로그래밍에서 파일을 읽고 분석하는 작업, 그리고 결과를 기록하여 보관하는 작업 등 매우 필수적이기 때문이죠. 여러분이 어떤 실행파일을 설치했는데 그 설정된 이력을 어떻게 보관할 수 있을까요? 눈치 채셨겠지만 파일의 형태로 어딘가에 저장하고 읽고 수정합니다. 텍스트 파일로든 이진 파일로든 말이죠. 이제 파이썬에서 파일을 어떻게 다룰수 있는지 여러 예를 보면서 알아보도록 하겠습니다.

1. 파일 열기 - open

파일을 사용하기 위해선 가장 먼저 해야하는 일이 파일을 여는 것입니다. 파일을 열때 사용하는 함수가 open함수입니다. 이름이 너무 직관적이라서 기억하기도 쉽군요. open은 두가지 인자를 받을 수 있습니다. 하나는 파일 이름, 다른 하나는 file open mode입니다. mode는 아래에 표를 참고해주세요.

mode desc
"r" Read를 뜻하며 파일을 수정하는 용도가 아니라 읽기 전용으로 엽니다. 파일이 없으면 에러가 발생합니다.
"w" Write를 뜻하며 파일을 수정할때 사용하지만, 이미 파일에 내용이 있다면 새로 다시 씁니다. 파일이 존재하지 않으면 새로 생성합니다.
"a" Append를 뜻하고 파일에 내용을 덧붙일때 사용하는 mode입니다. "w" 모드는 새롭게 덮어쓰는 것이고, "a" 모드는 뒤에 추가한다는 점이 다릅니다. 역시 파일이 존재하지 않으면 새롭게 생성합니다.
"x" Create를 의미하며 파일을 생성합니다. 파일이 존재하면 에러를 반환합니다.

 

여기에 추가적으로 파일이 이진 파일이냐, 사람이 읽을 수 있는 텍스트 파일이냐에 따른 mode도 존재합니다.

mode desc
"t" Text를 뜻하며 텍스트 모드로 파일을 엽니다. open에서 mode를 지정하지 않으면 테스트 모드로 읽습니다.
"b" Binary를 뜻하며 이진 파일을 읽습니다. 예를 들면 이미지같은 파일을 의미하는 것이죠.

 

open은 호출이 완료되면 파일 객체를 반환해줍니다. 그리고 이 객체를 통해서 읽기, 쓰기 작업이 이루어질 수 있죠. 파일에 대한 작업을 완료하면 파일 객체의 close 메소드로 받드시 닫아주어야합니다.

※close() 를 반드시 해야하는 이유

보통 close()를 안하시고 중요하게 생각하지 않는 사람들이 많은데 그것은 우리들의 프로그램이 금방 끝나기 때문입니다. 아주 빈번하게 발생하는 문제점은 이런 상황입니다. 한글 파일을 연 상태에서 그 파일을 삭제시켜 보세요. 어디에서 열려있다고 하면서 삭제 동작을 하지 않습니다. 마찬가지로 프로그램 내에서 파일을 다 사용했는데 열어놓으면 다른 쓰레드나 프로세스가 쓸 수 없는 상황이 발생합니다. 이해하시겠죠? 아래 영화폴더는 저의 보물창고입니다.

 

아래의 코드는 exam.txt라는 파일을 읽기 전용으로 텍스트 모드로 열고 닫는 예를 보여줍니다.

f = open("exam.txt","rt")
f.close()

 

2. 파일 쓰기 - write, writelines

write를 통해서 파일에 내용을 쓸 수 있습니다. 이때 여러 줄을 쓸때는 List 자료형이나 Tuple을 사용할 수 있는 writelines 메소드도 존재합니다. 아래는 파일에 문자열을 쓰는 코드의 사용방법을 보여줍니다.

f = open("exam.txt","wt")
#일반적으로 쓰는 write
f.write("============write test===============\n")

# 리스트로 한번에 넣어버리기
lines = ["write\n","list\n","lines\n"]
f.writelines(lines)

# 튜플로 한번에 넣어버리기
lines = ("write\n","tuple\n","lines\n")
f.writelines(lines)

f.close()

 

위 코드를 작성하고 실행하면 프로젝트와 같은 디렉토리에 파일이 생겨납니다. 우리가 썼던 그 텍스트 내용인것을 확인할 수 있습니다.

write

 

3. read, readline

이제 이 파일을 읽어보도록 할까요? 읽기 위해서는 파일 객체의 read류의 메소드들을 사용하여 읽을 수 있습니다. read는 기본적으로 한글자씩 읽어오는 메소드입니다. 활용하는 방식은 아래와 같이 파일의 내용이 없을때까지 출력합니다. read()는 더 이상 읽을 내용이 없으면 빈 문자열을 반환합니다. 그리고 컴퓨터 공학에서는 이것을 파일의 끝(EOF: End Of File)이라고 합니다. C언어와 같은 언어에서는 EOF는 -1이라는 것은 그냥 참고만 하세요.

f=open("exam.txt","rt")
while True:
    c = f.read()
    if c == '':
        break
    print(c, end='')

f.close()
============write test===============
write
list
lines
write
tuple
lines

 

혹은 그냥 줄 단위로 가져오고 싶지는 않으신가요? 그럴땐 readline을 사용하여 가져올 수 있습니다. readline 역시 더 이상 읽을 데이터가 없다면 빈 문자열을 반환합니다.

f=open("exam.txt","rt")

while True:
    line = f.readline()
    if line == '' :
        break
    print(line, end='')

f.close()

 

writelines와 마찬가지로 리스트 형태로 여러 줄들을 읽어올 수도 있습니다. 그래서 for문으로 그 줄들을 순회할 수 있습니다.

f=open("exam.txt","rt")

lines = f.readlines()
for line in lines:
    print (line, end='')

f.close()

 

4. 파일 삭제 - remove

파일을 생성하고 쓰는 방법은 알았는데, 파일을 삭제하려면 어떤 방법으로 삭제를 할까요? os 모듈을 통해서 파일이 존재하는지 확인할 수 있고 삭제할 수 있습니다. 아래 코드는 해당 파일이 존재한다면 삭제하고, 아니라면 존재하지 않는 다는 출력을 해주지요. 

import os
if os.path.exists("exam.txt"):
  os.remove("exam.txt")
else:
  print("not exist")

 

해당 코드를 실행하면 우리가 지금까지 썼던 exam.txt 파일은 삭제되었음을 알 수 있습니다.

 

여기까지 파이썬으로 파일을 다루는 아주 기초적인 활용 예제들을 보았습니다. 파이썬이 다른 언어에 비해서 파일을 다루는 게 단순한 편입니다. 잘 숙지하시고 연습많이 하세요.

반응형
블로그 이미지

REAKWON

와나진짜

,

datetime 모듈

파이썬에서 날짜와 관련된 데이터를 다룰때에는 datetime이라는 모듈을 import 하여 사용해야합니다. datetime은 날짜와 관련된 여러가지 메소드들을 가지고 있습니다.

 

현재 시간 구하기(now)

현재 시간을 구하려면 아래와 같이 간단한 한줄만 사용하여 나타낼 수 있습니다. now()는 datetime 객체를 반환하여 줍니다.

import datetime

d = datetime.datetime.now()
print (d)
2021-04-18 16:50:43.895283

 

datetime 객체는 년(year), 월(month), 일(day), 시(hour), 분(minute), 초(second)의 필드를 가지고 있습니다. 그래서 필요한 시간의 부분만 사용할 수도 있습니다.

d = datetime.datetime.now()
print (d.year,'년 ', d.month,'월 ', d.day,' 일')
print (d.hour,'시 ',d.minute,'분 ',d.second,'초')
2021 년  4 월  18  일
16 시  58 분  38 초

 

datetime 객체 구하기

위의 현재 시간을 now라는 메소드로 구할 수 있지만 우리가 직접 지정한 날짜를 datetime 객체로 가져오기를 원한다면 datetime 클래스의 생성자를 이용하여 가져올 수 있습니다.'

wuhan_covid19 = datetime.datetime(2019,12,12)
print (wuhan_covid19)
2019-12-12 00:00:00

 

위에서는 날짜만 지정했지만 시간도 같이 지정해줄 수 있습니다.

wuhan_covid19 = datetime.datetime(2019, 12, 12, 3, 3, 3)
print (wuhan_covid19)
2019-12-12 03:03:03

 

strftime() 메소드로 원하는 날짜 형식대로 출력

datetime은 날짜를 형식화해주는 메소드가 존재하는데 메소드명은 strftime(String Format Time)입니다. 메소드에 포맷 문자를 전달하여 원하는 날짜의 형태를 문자열로 가져올 수 있습니다.

포맷 코드는 아래의 표에 정리하였으니 맞는 format code를 조합하여 사용하면 됩니다.

Format Code

포맷 코드 설명
%a 요일을 짧게 표시합니다. Sun
%A 요일을 길게 표시합니다. Sunday
%w 요일을 숫자로 표시합니다. 일요일을 0부터 시작하여 토요일은 6입니다. 0
%d 날(day)을 출력합니다. 1부터 31까지가 있겠죠 18
%b 월을 영어로 짧게 출력해줍니다. Apr
%B 월을 영어로 길게 출력해줍니다. April
%m 월을 숫자로 표현합니다. 04
%y 년을 짧게 숫자로 표시합니다 21
%Y 년을 길게 숫자로 표시합니다. 2021
%H 시간을 24시간의 표현 방식(00-23)으로 숫자로 표시합니다. 18
%I 시간을 0-12시 표시 방법으로 표시합니다 6
%p 오전(AM), 오후(PM)을 표시합니다. PM
%M 분(0 - 59)을 표시합니다. 38
%S 초(0 - 59)를 표시합니다. 55
%f microsecond단위를 표시합니다. 545433
%j 일년중 몇번째일인지 나타냅니다. 108
%U 일년 중 몇번째 주 인지 나타내니다. 이 포맷에서 일요일은 일주일의 시작입니다. 일년은 52주, 53주입니다. 108
%W 일년 중 몇번째 주 인지 나타내는 것은 %U와 같지만 일주일의 시작을 월요일로 정합니다. 108
%c Local version의 날짜와 시간을 나타냅니다. Sun Apr 18 17:26:26 2021
%x Local version의 날짜만 나타냅니다. 04/18/21
%X Local version의 시간만 나타냅니다. 17:26:26

 

아래는 strftime을 통해서 시간을 출력해주는 예제 코드입니다.

now = datetime.datetime.now()

print (now.strftime("%Y/%m(%B)/%d %A %p %I:%m:%S, 일년 중 %U 번째주, 일년 중 %j번째 날 "))
print (now.strftime("%c"))
print (now.strftime("%x"))
print (now.strftime("%X"))
2021/04(April)/18 Sunday PM 05:04:51, 일년 중 16 번째주, 일년 중 108번째 날 
Sun Apr 18 17:30:51 2021
04/18/21
17:30:51

 

datetime 연산

datetime은 연산도 가능한데요. datetime끼리 더하기, 빼기 연산이 가능합니다. 이때 결과는 timedelta의 객체 형태로 반환됩니다.

import datetime

birthday = datetime.datetime(1988,12,11)
now = datetime.datetime.now()
elapsed = now - birthday
print (elapsed)
11816 days, 17:39:16.585322

 

여기까지 파이썬에서 날짜를 다루는 방법을 알아보았습니다.

반응형
블로그 이미지

REAKWON

와나진짜

,

파이썬은 문자열을 다룰때 다양한 형태로 문자열을 다룰 수 있습니다. C나 Java같은 언어에서는 문자열을 쌍따옴표로 둘러싸서 문자열을 표현하지만 파이썬은 ', ", ''' 로 둘러싸서 문자열을 표현할 수 있죠. 이렇게하는 이유는 ', " 가 문자열에 글자로 표현될 때 문자열의 종료나 시작으로 인식하지 않게 하기 위함입니다.

str1 = 'python'
str2 = "python"
str3 = '''python'''

print (str1, str2, str3)

str1 = '"python"'
str2 = "'python'"
str3 = '''"python"'''

print ( str1, str2, str3)
python python python
"python" 'python' "python"

 

이제부터 파이썬의 문자열에 대해서 알아보도록 하겠습니다. 

문자열 포맷

- 포맷 문자를 통한 문자열 포맷

C와 같이 문자열에 다른 데이터를 포함시키려면 format 문자를 사용해서 정수든, 글자든 입력받을 수가 있죠.

포맷 문자 설명
%d 10진수 정수 (Decimal)
%c 문자 (Character)
%f 부동 소수 (Floating Point)
%o 8진수 정수 (Octal)
%x 16진수 정수 (Hexadecimal)
%s 문자열 (String)
%% % 문자

 

C에서 지원하는 무자열 형태의 format은 거의다 지원한다고 보시면됩니다. 그렇기 때문에 아래의 링크를 통해서 더 많은 문자열 포맷을 활용하는 방법을 알아보시기 바랍니다.

reakwon.tistory.com/169

 

[C언어] 출력 형식(format) 총정리 (Feat. sprintf, fprintf) - 일정한 간격으로 문자열 출력 예제 까지

C언어의 다양한 출력 문자들 C언어에서 다양한 출력 형식을 지원합니다. 우리가 너무나 잘 알고 있는 부호있는 정수형은 %d, 문자열 출력은 %s 등이 그 출력형식인데요. 오늘은 자세하게 한번 총

reakwon.tistory.com

 

형식을 지정할 데이터는 문자열 끝 %를 이용해서 나열해줍니다.

print ('구구단 2단')
for i in range(10):
    print ('%d * %d = %d' % (2, i, 2*i) )
구구단 2단
2 * 0 = 0
2 * 1 = 2
2 * 2 = 4
2 * 3 = 6
2 * 4 = 8
2 * 5 = 10
2 * 6 = 12
2 * 7 = 14
2 * 8 = 16
2 * 9 = 18

 

- format함수를 통한 문자열 포맷

이와 같은 방식은 우선 자료형에 따른 포맷 문자를 알고있어야하는 단점이 있습니다. 파이썬 3부터는 포맷을 알고 있지 않아도 사용할 수 있는 방법은 format함수를 사용할 수 있습니다. 여기서 중괄호를 이용합니다. 숫자를 입력하여 차례대로 입력받을 수도 있고, 명시적으로 이름을 지정해서 사용할 수도 있습니다.

str = 'str.{} example'.format('format')
print(str)
                                                #  {0}      {1}        {2}          {3}
str = 'SELECT {0} FROM {1} WHERE {2} = {3}'.format('*', 'accounts', 'email', 'reakwon@gmail.com')
print (str)

str = 'name : {name}, age : {age}'.format(name='kim',age=22)
print (str)
str.format example
SELECT * FROM accounts WHERE email = reakwon@gmail.com
name : kim, age : 22

 

- f String을 사용한 문자열 포맷

문자열을 저장할때 가장 맨 앞에 f를 준 후 변수명을 그대로 문자열에 중괄호로 입력하면 그 변수명의 데이터가 그대로 문자열에 입력됩니다. f는 format의 약자라는 점은 참고해주세요. 이 f string은 파이썬 3.6부터 지원합니다.

name = 'shin'
age = 22
score = 80

result = f'name : {name}, age : {age}, score : {score}'
print (result)
name : shin, age : 22, score : 80

 

산술 연산도 할 수 있습니다.

a = 10
b = 20

fstr = f'{a} * {b} = {a*b}, {a} + {b} = {a+b}'
print (fstr)
10 * 20 = 200, 10 + 20 = 30

 

함수의 반환값도 쓸수 있죠.

def mult(a, b):
    return a*b

def add(a, b):
    return a+b

a = 5
b = 9
fstr = f'{a} * {b} = {mult(a,b)}, {a} + {b} = {add(a,b)}'
print (fstr)
5 * 9 = 45, 5 + 9 = 14

 

문자열 메소드

문자열 메소드는 엄청 많은데, 그 중에서 몇가지 문자열 메소드를 알아보도록 하겠습니다. 문자열은 아래의 문자열을 사용해보지요.

paul_rand = 'Do not to be original, just-try-to-be-good.'

 

- 문자열 길이 : len

문자열 내장 메소드는 아니지만 길이를 알고자 하는 경우 len 내장 함수를 쓰면 됩니다.

print(len(paul_rand))
43

 

- 문자수 세기 : count

문자나 문자열의 수를 새려면 count 메소드를 사용하여 확인할 수 있습니다.

print (paul_rand.count('t'))        #t문자 세기
print (paul_rand.count('to'))      #to 문자열 세기
5
2

 

- 대소문자로 변경 : upper, lower, casefold

알파벳을 모두 소문자로 변경하려면 lower 메소드 사용하면 되는데 소문자로 변경하는 메소드는 casefold라는 메소드도 있습니다. 반대로 모두 대문자로 변경하려면 upper를 사용하면 됩니다.

print (paul_rand.upper())   #모두 대문자로 변경
print (paul_rand.lower())   #모두 소문자로 변경
print (paul_rand.casefold())
DO NOT TO BE ORIGINAL, JUST-TRY-TO-BE-GOOD.
do not to be original, just-try-to-be-good.
do not to be original, just-try-to-be-good.

 

- 문자열 분리 : split

문자열을 공백, 또는 지정된 나누려고 구분된 구분자에 따라서 문자열을 쪼개고 싶다면 split 메소드를 사용할 수 있습니다. 인자를 넣어주지 않는다면 공백을 기준으로 나누고, 지정한 문자열을 넘겨주면 그 문자열을 기준으로 문자열을 나눕니다. 쪼개어진 문자열들은 리스트 형태로 넘겨줍니다.

tokens = paul_rand.split()
print (tokens)

tokens = paul_rand.split('-')
print (tokens)
['Do', 'not', 'to', 'be', 'original,', 'just-try-to-be-good.']
['Do not to be original, just', 'try', 'to', 'be', 'good.']

 

- 문자열 공백 지우기 : strip, lstrip, rstrip

문자열에 공백을 제거하려면 strip 메소드를 사용하면 됩니다. 특별히 왼쪽 공백은 lstrip, 오른쪽 공백은 rstrip을 사용하면 됩니다.

str = '  __name__  __main__    '

print (str.lstrip())    # 왼쪽 공백 제거
print (str.rstrip())    # 오른쪽 공백 제거
print (str.strip())     # 양쪽 공백 제거
__name__  __main__    
  __name__  __main__
__name__  __main__

 

- 문자열 위치 : find, index, rfind, rindex

문자열에서 특정 문자열이 어느 위치에 있는지 확인하려면 find와 index를 사용하면 됩니다. 이때 가장 첫번째로 등장한 위치를 반환합니다. find와 index의 차이점은 문자를 찾지 못할때는 에러를 발생시키느냐 마냐입니다. find는 못찾으면 -1을 반환하고 index는 에러를 발생시킵니다.

print ('o : ', paul_rand.find('o'))
print ('. : ', paul_rand.index('.'))
print ('original : ', paul_rand.find('original'))
print ('just : ' ,paul_rand.index('just'))

print ('6 :', paul_rand.find('6'))      #없는 문자열의 경우 -1 반환
print ('6 :', paul_rand.index('6'))     #없는 문자열의 경우 에러

o :  1
. :  42
original :  13
just :  23
6 : -1
Traceback (most recent call last):
  File "C:\Users\grjwu\PycharmProjects\pythonProject1\main.py", line 9, in <module>
    print ('6 :', paul_rand.index('6'))
ValueError: substring not found

 

또는 start와 end 인덱스를 지정하게 되면 그 부분에 대해서만 찾아오게 됩니다.

print (paul_rand.find('to',4,9))    # 글자위치 4부터 9 전까지 탐색
print (paul_rand.index('or',10,19)) # 글자위치 10부터 19 전까지 탐색
7
13

 

왼쪽이 아니라 오른쪽에서 찾아보고 싶다면 rfind와 rindex를 사용하면 됩니다. 이때 결과는 위의 index와 find와 동일하며 오류내는 것도 동일합니다.

good = 'good, good, good~'

print ('o : ', good.rfind('o'))
print ('g : ', good.rindex(','))
o :  14
g :  10

 

 

- 문자열 변경 : replace

특정 문자열을 변경하고 싶다면 replace메소드를 사용하여 바꿀 수 있습니다.

print (paul_rand.replace('to','TO'))
Do not TO be original, just-try-TO-be-good.

 

- 특정 문자열로 시작하느냐 끝나느냐 - startswith, endswith

우리가 지정한 문자열로 시작하느냐를 알아보고 싶다면 startswith, 끝이 나는가를 알아보려면 endswith 메소드를 사용하면 됩니다. 지정된 문자열로 시작, 끝이 나면 True를, 아니면 False를 반환합니다.

print (paul_rand.startswith('Do'))
print (paul_rand.endswith('.'))

print (paul_rand.startswith('The'))
print (paul_rand.endswith('!'))
True
True
False
False

 

- 문자열 삽입 : join

특정 문자열을 문자마다 삽입하고 싶다면 join을 사용하면 됩니다. 글자마다 우리가 지정한 문자열이 삽입되고 만약 단어마다 문자열 삽입을 원한다면 리스트 형태의 문자열 리스트를 전달해주면 됩니다.

 

str = 'ABCDE'
print ("=".join(str))

str = ['Apple','Banana','Cherry']
print (', '.join(str))
A=B=C=D=E
Apple, Banana, Cherry

 

- 탭 간격 조정 : expandtabs

탭의 간격을 조정하는 메소드는 expandtabs입니다. 

str = "h\te\tl\tl"
print (str)
print (str.expandtabs(2))
print (str.expandtabs(4))
print (str.expandtabs(10))
h	e	l	l
h e l l
h   e   l   l
h         e         l         l

 

여기까지 파이썬 문자열의 활용방법과 메소드 들에 대해서 알아보았습니다. 여기서 소개하지 않은 메소드도 많이 있으므로 그때 그때 구글링하여 사용하시기 바랍니다.

반응형
블로그 이미지

REAKWON

와나진짜

,

리스트(List) 자료형

파이썬에는 다른 언어와 비슷하게 자료를 배열과 같이 사용하는 방법이 있습니다. 다른 언어와 같이 배열과 비슷한 특징이 있으나 여기서 추가 기능이 많이 추가 되어 있죠. 어떤 것들이 있는지 이 포스팅에서 정리하도록 하겠습니다.

1. 리스트 초기화와 접근(Indexing)

기본 초기화

리스트는 대괄호([ ]) 안에 리스트의 요소들이 저장됩니다. 대괄호하면 뭔가 배열이 생각나지 않으신가요? 대괄호에 저장하고싶은 원소를 쉼표로 구분하여 넣어주면 끝입니다.

intList = [1, 2, 3, 4, 5]
strList = ["reakwon", "tistory", "com"]
print (intList)
print (strList)
[1, 2, 3, 4, 5]
['reakwon', 'tistory', 'com']

 

원소 접근

만약 리스트의 원소에 접근하여 읽거나 값을 변경하고 싶다고 하면 인덱스를 사용하여 접근할 수 있습니다. 맨 앞 원소는 0번, 맨 뒤의 원소는 리스트의 길이 - 1이 되죠. 인덱스에는 부호 -가 붙을 수 있는데, 이때는 뒤의 인덱스를 의미하지만 -1부터 시작합니다. -1은 맨 마지막 원소의 인덱스입니다. -2는 맨 마지막 2번째 원소를 의미하게 되죠.

intList = [1, 2, 3, 4, 5]
strList = ["reakwon", "tistory", "com"]
print (intList[0], intList[1], intList[-1], intList[-2])
print (strList[0], strList[-1])

#strList 마지막의 원소 값 변경
strList[2] = 'COM'
print (strList)
1 2 5 4
reakwon com
['reakwon', 'tistory', 'COM']

 

슬라이싱 - i인덱스부터 j인덱스까지 접근 : [i : j ] 

그렇다면 만약 전체 원소에 접근하지말고 특정 인덱스(begin)부터 특정 인덱스 -1 (end) 까지 원소에 접근하고 싶다면 콜론(:)을 이용할수 있습니다. 마지막 처음 인덱스를 지정하지 않으면 0번부터 시작, 마지막 인덱스를 지정하지 않는다면 리스트의 끝까지를 의미합니다. 이 같은 법칙에 따라서 [:]은 리스트의 전체를 의미합니다. 

intList = [1, 2, 3, 4, 5]
strList = ["reakwon", "tistory", "com"]

print (intList[1:2])    #1번 원소부터 2번 원소 전까지
print (strList[1:])     #1번 원소부터 끝까지

# [1, 2, 3, 4, 5] 의 1번 인덱스부터 3-1=2번 index까지 값을 변경
intList[1:3] = [12, 13]
print(intList)
[2]
['tistory', 'com']
[1, 12, 13, 4, 5]

 

2. 리스트 원소 삭제

읽기와 변경뿐만 아니라 원소를 아예 리스트에서 삭제하고 싶다면 del 이라는 함수를 사용해서 삭제할 수 있습니다. 마찬가지로 슬라이싱으로 한꺼번에 리스트 원소를 삭제할 수도 있습니다.

intList = [1, 2, 3, 4, 5]
strList = ["reakwon", "tistory", "com"]

del intList[:3]	# 3번 인덱스 이전까지 원소 모두 삭제
del strList[2]	# 2번 인덱스 원소 삭제

print(intList)
print(strList)
[4, 5]
['reakwon', 'tistory']

 

 

 

3. 다중 리스트

리스트안에 리스트가 포함이 될 수 있습니다. 다중 리스트에 접근할때는 인덱스를 추가한 리스트만큼 인덱스를 반복하면 됩니다. 만약 아래와 같이 mergedList에 intList에 포함이 된다면 intList에 존재하는 원소에 접근하려할때는 인덱스를 어떻게 지정하면 좋을까요? 모든 것을 포함한 index와 그 다음 리스트가 포함한 원소의 index , ... 마지막 리스트의 원소 index 이런식으로 접근할 수 있습니다.

intList = [1, 2, 3, 4, 5]
        # [0][1][2][3][4]
mergedList = ["reakwon", "tistory", "com",intList]
            #   [0]         [1]      [2]    [3]

print(mergedList[0], mergedList[1], mergedList[2], mergedList[3][0], mergedList[3][1], mergedList[-1][-1])
reakwon tistory com 1 2 5

 

4. 리스트의 반복

리스트를 반복하고 싶으면 * 연산자를 사용해서 반복할 횟수를 적어주면 됩니다.

ls = ['-','=']
repeat = ls * 5
print (repeat)
['-', '=', '-', '=', '-', '=', '-', '=', '-', '=']

 

5. 리스트의 내장 함수

리스트는 인덱스 접근, 변경, 삭제가 가능할뿐만 아니라 때에 따라 맞게 사용할 수 있는 함수를 내장하고 있습니다. 여기서 어떤 함수들이 있나 살펴보도록 하겠습니다.

 

리스트의 길이 - len

리스트의 내장함수는 아닌데, 리스트의 길이를 구하고 싶을때는 len 함수를 사용하여 구할 수 있습니다.

numbers = [5, 2, 3, 5, 6, 7, 1, 1, 1]   #9개 원소

print ("numbers 길이 : ", len(numbers))
numbers 길이 :  9

 

정렬 - sort

리스트는 sort 함수 한번으로 원소를 정렬할 수 있습니다. 이때 주의할 점은 리스트 내의 자료형은 같은 자료형이어야합니다. 정수형, 문자열을 섞어서 사용할 수는 없습니다. 숫자일때 기본 정렬은 오름차순, 문자열일때 정렬은 기본 사전순이 됩니다. 이때 대소문자를 구분하여 대문자가 먼저 나옵니다. 이유는 소문자의 ASCII코드보다 대문자가 더 작은 코드이기 때문이죠.

numbers = [5, 2, 3, 5, 6, 7, 1, 1, 1]
strs = ['apple', 'computer', 'python', 'list', 'C++','Java', 'banana']

numbers.sort()  #정수 정렬
strs.sort()     #문자열 정렬

print(numbers)
print(strs)

 

[1, 1, 1, 2, 3, 5, 5, 6, 7]
['C++', 'Java', 'apple', 'banana', 'computer', 'list', 'python']

 

리스트의 순서를 거꾸로 저장 - reverse

리스트 순서를 거꾸로 만들고 싶다면 reverse를 이용해서 순서를 거꾸로 만들 수 있습니다. 이를 응용해서 위의 정렬된 리스트를 내림차순으로 만들고 싶다면 sort 이후 reverse를 사용하면 되겠죠?

numbers = [5, 2, 3, 5, 6, 7, 1, 1, 1]

numbers.sort()  #정수 정렬
numbers.reverse()   #거꾸로 뒤집은 리스트

print(numbers)
[7, 6, 5, 5, 3, 2, 1, 1, 1]

 

리트스에 포함된 원소의 갯수 - count

혹시 리스트에 우리가 찾는 값이 있는지, 있다면 몇개나 존재하는지 확인하고 싶다고 한다면 count 함수를 사용하면 됩니다.

numbers = [30, 19, 100, 34, 123, 51, 0, 0, -1]
strings = ['java', 'python', 'programming', 'algorithm', 'null', 'null']

print ('0은 몇개? :', numbers.count(0))
print ('null은 몇개? :', strings.count('null'))

0은 몇개? : 2
null은 몇개? : 2

 

마지막에 요소 추가 - append

append는 덧붙이기라는 뜻이 있어 보통 파일에 덧붙이기로 열때 옵션으로 'a'를 사용하는 것도 파일에 추가 기록하기 위함입니다. 기존의 원소를 삭제하지 않고 마지막에 추가합니다

ls = ['hello','world']

ls.append('!!')
ls.append('This')
ls.append('is')
ls.append('python')
print (ls)
['hello', 'world', '!!', 'This', 'is', 'python']

 

리스트를 끝에 추가하는 것도 가능합니다.

animals = ['dog', 'cat', 'orca']

animals.append(['monkey','elephant'])

print(animals)
['dog', 'cat', 'orca', ['monkey', 'elephant']]

 

리스트의 확장 - extend

append에서 끝에 리스트를 추가할 수 있었는데 이를 접근하려면 중첩 인덱싱을 사용해야합니다. 이렇게 말고 아예 원소들 자체를 리스트에 포함시키고 싶을때는 extend를 활용하여 달성할 수가 있습니다. extend는 반드시 리스트 자료형을 전달받습니다.

 

animals = ['dog', 'cat', 'orca']

animals.extend(['monkey','elephant'])

print(animals)
print(animals)

 

중간에 요소 삽입 - insert

마지막이 아니라 중간 어느 지점에 원소를 추가하려면 insert 함수를 사용하면 됩니다. 어느 위치에 삽입 어떤 데이터를 삽입할 것이냐를 알아야하기 때문에 인자를 2개받습니다.

www = ['world','web']

www.insert(1,'wide')    # 1번째에 'wide' 원소 추가
print(www)

 

append와 같이 중간에 리스트 자체를 추가할 수도 있습니다.

PS4Titles = ['WWZ', 'Tekken','GTA','Red Dead Redemption', 'FIFA']
PS4Titles.insert(2,['The Last Of Us Part 1', 'The Last Of Us Part Golf'])

print(PS4Titles)
['WWZ', 'Tekken', ['The Last Of Us Part 1', 'The Last Of Us Part Golf'], 'GTA', 'Red Dead Redemption', 'FIFA']

 

원소 삭제 - remove

원소를 삭제하려면 remove를 사용해서 삭제할 수 있습니다. 이때 인덱스가 아닌 원소 자체를 넘겨줘야합니다.

countries = ['korea','japan','china','US','UK','france','vietnam']

countries.remove('japan')
countries.remove('china')

print(countries)
['korea', 'US', 'UK', 'france', 'vietnam']

 

혹은 리스트 자체를 삭제할 수도 있습니다. 

countries = ['korea',['japan','china'],'US','UK','france','vietnam']

countries.remove(['japan','china'])

print(countries)
['korea', 'US', 'UK', 'france', 'vietnam']

 

원소 전부 삭제 - clear

리스트의 내용을 전부 비워내고 싶다면 clear 함수를 사용할 수 있습니다. 허나 del로도 같은 기능을 할 수 있습니다.

www = ['world','web']

www.clear() #del www[:]와도 같은 동작

print (www)
[]

 

원소를 가져온 후 삭제 - pop

pop은 원소를 가져오고 난 이후에 리스트에 삭제하는 기능을 합니다. 만약 아무런 인자를 넘겨주지 않는 경우 마지막 원소를 가져오고 리스트에서 삭제하고, 그렇지 않고 인덱스를 넘겨주면 그 인덱스의 원소를 가져온 후 삭제합니다.

countries = ['korea','japan','US','UK','france','vietnam', 'china']

countries.pop()     #마지막 원소 삭제
countries.pop(1)    #1번 원소 삭제

print(countries)
['korea', 'US', 'UK', 'france', 'vietnam']

 

원소의 인덱스 찾기 - index

값이 어느 인덱스에 저장되어있는지 확인하려면 index를 사용하여 확인할 수 있습니다. 가장 1번째는 찾을 값이 주어지집니다. 이후 다른 인자가 없다면 전체 리스트를 검색하여 찾아냅니다. 그렇지 않고 시작 인덱스와 끝 인덱스를 지정하여 찾아낼 수도 있습니다. 

languages = ['C++', 'Java', 'Python', 'C', 'C#', 'Kotlin']

print (languages.index('C++'))
print (languages.index('Kotlin',0,4))

0
Traceback (most recent call last):
  File "C:\Users\grjwu\PycharmProjects\pythonProject1\main.py", line 5, in <module>
    print (languages.index('Kotlin',0,4))
ValueError: 'Kotlin' is not in list

 

만약 인덱스를 찾아올 수 없다면 위의 에러를 만나게 되죠.

간단하게 리스트를 파이썬에서 어떻게 활용하는지 확인해보았습니다.

 

반응형
블로그 이미지

REAKWON

와나진짜

,

C언어의 다양한 출력 문자들

C언어에서 다양한 출력 형식을 지원합니다. 우리가 너무나 잘 알고 있는 부호있는 정수형은 %d, 문자열 출력은 %s 등이 그 출력형식인데요. 오늘은 자세하게 한번 총 정리하는 포스팅을 정리하도록 하겠습니다. 마지막에는 단순 화면에 출력하는 것이 아닌 변수에 저장하는 방법, 파일에 출력하는 방법을 알아보도록 하겠습니다. 더불어 이 형식들과 소개한 함수를 이용해서 ls가 출력하는 형식처럼 일정한 간격으로 보기 좋게 문자열을 출력하는 방법도 소개합니다.

 

출력 문자(Format Character)

출력 형식 설명 출력 예
%d 우리가 흔히 알고 있는 부호 있는 정수형을 출력해줍니다. printf("%d",-150); -150
%c 문자열 하나를 출력해줍니다. printf("%c",'A');  A
%p 주로 주소를 출력합니다. 메모리 크기만큼 자릿수가 채워집니다. 출력될때는 16진수로 표시됩니다. int a;
printf("%p",&a);
0177F95C
%x 정수를 16진수로 출력합니다. %x를 보시면 소문자인데, 16진수로 표시될때 알파벳은 소문자로 표시됩니다. printf("%x",10); a
%X 위의 %x와 동일하나 알파벳이 대문자로 표시됩니다. printf("%X",10); A
%o 8진수로 출력합니다. printf("%o", 8888); 21270
%s 문자열을 출력합니다. '\0'인 NULL문자를 만날때까지 출력이 됩니다. printf("%s","hello, world"); hello, world
%u 부호없는 정수 출력입니다. 즉, 양수를 출력하는 포맷입니다. signed bit을 정수 데이터로 취급한다는 것이죠. 만약 -1이라는 정수가 2의 보수를 거치는 과정은 이렇습니다.  0000 0000 0000 0000 0000 0000 0000 0001 -> 1111 1111 1111 1111 1111 1111 1111 1110 + 1-> 1111 1111 1111 1111 1111 1111 1111 1111 = 4294967295 이 되죠. 부호가 있다면 -1이지만 부호없으면 4294967295라고 이렇게 표시가 된다는 뜻이에요. printf("%u",1);
printf("%u",-1);
1
4294967295
%ld 부호있는 long 형 정수 출력입니다. printf("%ld", 1L); 1
%lld 부호있는 long long 형 정수 출력입니다. printf("%lld", 5294967295); 5294967295
%lu 부호없는 long 형 정수 출력입니다. printf("%lu", -12345); 4294954951
%llu 부호없는 long long 형 정수 출력입니다. printf("%llu",-12345); 18876570244468679
%f, %lf 두 개 모두 실수형을 출력합니다. 6자리까지 출력이 되면 그 이하는 반올림처리됩니다. 모자라면 0으로 채웁니다.
출력할때는 상관없지만 입력 받을때는 float 자료형에 넣을시 %f, double에 입력받을때는 %lf를 사용합니다.

float f;
double d;
scanf("%f",&f);
scanf("%lf",&d);
--------------------------
printf("%f",0.1234f);
printf("%f",0.1234567f);
0.123400
0.1234567
%e 실수를 지수 표기법으로 소문자로 표시합니다.  printf("%e", 0.11223344f); 1.122334e-01
%E 실수를 지수 표기법으로 표시할때 대문자를 사용합니다. printf("%E", 0.11223344f); 1.122334E-01

 

형식 정렬 (Format Alignment)

'%' 문자와 포맷 문자(d, x, u 등) 사이에 부호와 숫자를 넣을 수 있는데, 숫자는 공간을 의미합니다. 만약 공간이 남으면 빈 자리 만큼 공백이 생깁니다. 이때 하고 부호('-')가 있으면 왼쪽 정렬, 아니면 오른쪽 정렬입니다. 예를 보면서 확인하세요.

출력 형식 출력 설명
printf("%-4d, %d, %4d", 10,11,12); 10  , 11,   12 4자리 공간을 확보하며 10은 왼쪽 정렬, 12는 오른쪽 정렬을 하여 출력하니다.
printf("%-4d, %d, %4d", 10000,11000,12000); 10000, 11000, 12000 4자리를 넘어갈 경우 일반 출력과 같습니다.
printf("%8x",0xFFEE)     ffee 8자리 확보하고 오른쪽 정렬합니다.
printf("%08x",0xFFEE) 0000ffee 8자리를 확보하고 빈 공간은 0으로 채웁니다.
printf("%02x %02x %02x", 0xE, 0xF1, 0xDEAD);  0e f1 dead 2자리를 확보합니다. 빈자리는 0으로 채우고 넘는다면 일반 출력과 같습니다.
printf("%.3f", 3.14f);
printf("%.3f", 3.141592f);
3,14
3.142
소수점 이하 3자리까지 출력합니다. 자리수가 넘어가지 않을 경우 0으로 채우며 넘어가면 3자리에서 반올림하여 출력합니다.
printf("%10.3f", 3.14f);      3.140 10자리 확보한 이후 수를 오른쪽 정렬로 출력합니다. -는 왼쪽 정렬이겠죠?
printf("%10s", "reakwon");    reakwon 10자리 확보 후 오른쪽 정렬로 문자열을 출력합니다.

 

n진수 문자 포함 출력

8진수나 16진수일때 n진수 문자를 앞에 표기할때 문자 '#'을 사용합니다. 여기서는 16진수만 예를 들어보겠습니다.

출력 형식 출력 설명
printf("%#x", 0xFF11); 0xff11 16진수 표기법인 0x까지 출력합니다.

 

%기호를 자체 출력

%는 정말 문자 그대로 출력하려면 %% 두번쓰면 됩니다. 

출력 형식 출력 설명
printf("%%JAVA_HOME%%bin"); %JAVA_HOME%bin %를 문자로 출력했습니다. %%를 사용하였죠.

 

우리는 단순히 화면에 출력했었죠? 하지만 가끔 프로그래밍을 하다가 보면 화면에 출력하는 방식이 아닌 변수에 담거나, 파일에 직접써야하는 일이 생깁니다. 그럴때 유용한 함수들이 존재하는데 여기서 알아볼 함수는 sprintf fprintf입니다. 

sprintf - 문자 배열에 형식 문자열 write

만약 콘솔에 출력하는 대신 일반 문자열에 저장하려면 어떻게 할까요? 이런 기능을 맡는 함수가 sprintf 함수입니다. sprintf의 첫 인수는 저장할 문자 버퍼(문자 배열), 두번째는 포맷 문자열, 세번째는 형식에 대응되는 데이터들입니다.

	char str[128];
	sprintf(str, "%d + %d = %d", 10, 20, 10 + 20);
	printf("%s\n", str);
10 + 20 = 30

 

sprintf 예제 ) 문자열을 일정한 간격으로 출력

리눅스 ls명령을 보면 신기하게도 문자열을 고른 간격으로 출력하는 것을 볼 수 있죠. 신기하지 않았나요? 나만 그런가.. 여기서 sprintf를 사용하여 흉내낼 수 있습니다. 

 

어떻게 흉내낼까요? 아래의 코드가 흉내내는 코드입니다. 설명은 주석으로 충분할 것 같습니다.

#include <stdio.h>
#include <string.h>
int main() {
	int i;
	char name[][100] = { "reakwon","kim","john","lee","yu","loooooooooooooooong","hello!","good!" };
	char format[128];	//출력 형식을 담을 문자열
	int longest = 0;	//가장 긴 문자열의 길이
	
	for (i = 0; i < 8; i++) {
		int len = strlen(name[i]);
		if (len > longest)		
			longest = len;
	}

	// '%' + '-' + %u = longest+3] + s -> %-[longest+3]s : 왼쪽 정렬된 문자 공간 longest+3 자리 확보
	sprintf(format, "%%-%us", longest+3);	
	for (i = 0; i < 8; i++) {
		if (i != 0 && i % 2 == 0) printf("\n");
		printf(format, name[i]);
	}

}

 

일정한 길이의 문자열 출력

 

fprintf - 파일에 형식 문자열 출력

콘솔에 출력하는게 아니라 파일에 출력하려면 fprintf함수를 사용하면 됩니다. sprintf와 사용법은 거의 비슷한데 첫 인자를 파일로 지정하면 됩니다. 어려운것은 없죠?

	FILE* fp = fopen("file.txt", "w");

	fprintf(fp, "%s, %s", "reakwon", "tistory");
	fclose(fp);

 

fprintf 사용법

 

여기까지 C언어에서 지원하는 출력 형식(format)을 알아보았고 콘솔에 출력하지 않고 문자 배열에 저장하는 함수인 sprintf와 파일에 출력해주는 유용한 함수 fprintf에 대해서 알아보았습니다. 유용한 함수이고 어려운 것은 없으니 기억해두셔서 적재적소에 쓰시면 되겠습니다.

반응형
블로그 이미지

REAKWON

와나진짜

,

자바 소켓 프로그래밍(Network Socket Programming)

네트워크에서는 크게 두가지 프로토콜이 있습니다. TCP와 UDP가 그것들이죠. TCP는 연결형에 신뢰성을 바탕을 둔 전송 프로토콜이고, UDP는 비연결성의 비신뢰성의 프로토콜입니다. 여기서 왜 TCP가 신뢰성이 있냐면 패킷 유실시에 다시 재전송을 하기 때문이죠. UDP는 그런게 없이 잃어버리든 말든 재전송따위는 없이 보냅니다. 

여기서는 자바를 통해 TCP를 이용해 서버와 통신하는 프로그램을 구현해보도록 하겠습니다. TCP를 통해서 프로그래밍을 할땐 Socket에 관한 이해가 필요한데, 아래의 링크를 통해서 개념을 잡고 오세요. 설명은 리눅스 C 소켓 프로그래밍을 설명하지만 기본 개념은 같습니다.

reakwon.tistory.com/81

 

[리눅스] 소켓(socket) 개념과 예제(connect, bind, listen, accept,send,recv 사용)

소켓(socket) 네트워크 통신을 하는 표준 방법으로 프로세스간 연결의 종점이라고 볼 수 있습니다. 기본적인 개념은 아래의 그림과 같습니다. 위의 그림은 TCP/IP에서의 인터넷 통신을 보여줍니다.

reakwon.tistory.com

 

서버-클라이언트 모델을 사용하기 때문에 서버용 프로그램과 클라이언트용 프로그램 두가지 메인 프로그램이 필요합니다.

Client는 GUI로 화면에 출력하고 서버는 Console 출력화면을 사용하도록 하겠습니다. 

 

서버 구현

Socket에 대한 개념을 충분히 이해하셨다면 이제 코드만 이해하면 됩니다. 아니, 방법만 알면 됩니다. 서버쪽에는 ServerSocket이라는 클래스를 이용하여 이것으로 클라이언트 연결이 올때까지 대기합니다. accept() 메소드가 바로 클라이언트 연결을 대기하는 메소드이고 연결이 성립될때까지 대기(컴퓨터 과학에서는 Block 된다고합니다.)합니다. accept는 연결이 되면 실제 통신하기 위해 Socket 객체를 넘겨주며 넘겨받은 Socket으로 실제 통신을 한다고 생각하시면 됩니다. 

그러니까 정리하자면 ServerSocket(개인적으로 부모 Socket이라 부릅니다.)은 Client를 받기 위한 소켓이고, 실제 데이터 송수신하는 소켓은 ServerSocket이 accept()하고 넘겨준 Socket(개인적으로 세끼 Socket이라고 합니다.)이라는 것이죠. 

아래의 코드는 포트번호 9999를 사용하며, 연결이 오면 소켓의 정보를 출력해주고 클라이언트에게 "Hello!! From Server"라는 메시지를 보낸 후 통신을 끊는 아주 간단한 서버의 코드입니다.

 

- TCPServer.java

public class TCPServer {
	
	public final static int SERVER_PORT=9999;
	public static void main(String[] ar) {
		ServerSocket ss=null;
		try {
			ss=new ServerSocket(SERVER_PORT);
			
		}catch(IOException e) {
			e.printStackTrace();
		}
		
		while(true) {
			try {
				System.out.println("Waiting connection...");
				Socket s=ss.accept();		//새끼 Socket 넘겨줌
				System.out.println("[ Connection Info ]");
				System.out.println("client address:"+s.getInetAddress());	//클라이언트 IP주소
				System.out.println("client port:"+s.getPort());			//클라이언트 포트 번호
				System.out.println("my port:"+s.getLocalPort());		//나(Server, Local)의 포트
				
				PrintWriter pw=new PrintWriter(new OutputStreamWriter(s.getOutputStream()));
				pw.println("Hello!! From server");
			
				pw.close();
				s.close();
			}catch(IOException e) {
				e.printStackTrace();
			}
		}
	}
}

 

클라이언트 구현

GUI 설정때문에 코드가 길어보이는 것뿐이니 사실 별거없습니다. 간단하게 클라이언트는 Swing 컴포넌트로 JTextArea에 Socket의 정보와 서버로부터 온 메시지를 출력해줍니다.  .정말 별거없죠? 

public class TCPClient extends JFrame{
	public final static int SERVER_PORT=9999;
	
	private Socket s;
	private JTextField messageField;
	private JTextArea messages;
	
	public TCPClient() {
		super(" TCP Client");
		messageField=new JTextField(40);
		messages=new JTextArea(10,50);
		
		messages.setBackground(Color.black);		//배경 검은색
		JScrollPane scrolledMessages=new JScrollPane(messages);	//스크롤 가능하도록
		
		this.setLayout(new BorderLayout(10,10));
		this.add("North",messageField);
		this.add("Center",scrolledMessages);
		
		messages.setEnabled(false);		//TextArea disactive
		
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setSize(620,400);
		this.setLocationRelativeTo(null);	//창 가운데 위치
		this.setVisible(true);
		
		
		connectServer();
	}
	
	public void connectServer() {
		String serverIP="127.0.0.1";
		
		try {
			Socket s=new Socket(serverIP,SERVER_PORT);
			messages.append("server port:"+s.getPort()+", my port:"+s.getLocalPort()+"\n");
			BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));
			messages.append(br.readLine());
			
			br.close();
			s.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static void main(String []ar) {
		new TCPClient();	
	}
}

 

위의 IP주소는 127.0.0.1인 이유는 저의 주소를 그대로 사용하게 만들도록 하기 위함입니다. 클라이언트는 포트번호 9999로 서버 소켓에 연결합니다. 아래는 서버와 클라이언트를 실행한 화면입니다. 

서버부터 실행하고 다음 클라이언트를 실행해야합니다. 서버와 클라이언트의 소켓정보가 일치됨을 확인할 수 있고 클라이언트는 서버로부터의 메시지를 잘 받아왔네요.

 

 

에코 서버-클라이언트 구현

클라이언트의 메시지를 그대로 돌려주는 서버를 우리는 에코 서버(Echo Server)라고 합니다. 그것을 구현해볼껀데요. 여기서 서버는 ServerSocket으로 새로운 통신을 확립시키면 새끼 Socket을 새로운 통신 쓰레드에 넘겨주며 작업을 처리하게 할것이고 다음 연결을 대기하게 만듭니다. 쓰레드와 소켓이 보통 같이 구현됩니다.

서버 구현


public class TCPServer {
	
	public final static int SERVER_PORT=9999;
	public static void main(String[] ar) {
		ServerSocket ss=null;
		try {
			ss=new ServerSocket(SERVER_PORT);
			
		}catch(IOException e) {
			e.printStackTrace();
		}
		
		while(true) {
			try {
				System.out.println("Waiting connection...");
				Socket s=ss.accept();		//새끼 Socket 넘겨줌
				
				new ServerThread(s).start();
				
			}catch(IOException e) {
				e.printStackTrace();
			}
		}
	}
}

class ServerThread extends Thread{
	
	private Socket s;
	private BufferedReader br;
	private PrintWriter pw;
	public ServerThread(Socket s) {
		this.s=s;
		try {
			br=new BufferedReader(new InputStreamReader(s.getInputStream()));	//Socket으로 Read용 Stream
			pw=new PrintWriter(new OutputStreamWriter(s.getOutputStream()));	//Socket으로 Write용 Stream
		}catch(IOException e) {
			e.printStackTrace();
		}
	}
	@Override
	public void run() {
		
		while(true) {
			String received;
			try {
				received = br.readLine();	//1. 받고
				System.out.println("server received :"+received);
				if(received==null||received.equals("quit")) {	//quit 또는 q가 오면 종료
					if(br!=null) br.close();
					if(pw!=null) pw.close();
					if(s!=null) s.close();
					return;
				}
				
				pw.println("Server Got Your Message : "+received);	//2. 보냄
				pw.flush();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

 

ServerThread의 생성자에서는 소켓을 넘겨받고 사용할 인풋,아웃풋 스트림을 생성합니다. 그리고 run() 메소드에서 이런작업을 진행하지요. 

 

1. 우선 클라이언트로부터 메시지를 읽어옵니다. 이때 BufferedReader를 사용하죠.

2. 메시지를 검사하고 다시 클라이언트로 서버의 메시지를 첨가해 다시 넘겨줍니다. 이때 PrintWriter를 사용합니다. 여기서 한가지 하는 실수가 뭐냐면 PrintWriter는 내부적으로 버퍼를 사용하기 때문에 버퍼가 다 차기전까지 내용을 출력하지 않습니다. 그래서 강제로 비워줘야하는데, 그 메소드가 flush()입니다. 이전의 맨 처음 코드에서 flush()를 하지 않은 이유는 PrintWriter의 close()메소드가 버퍼를 비우고 스트림을 닫기 때문에 굳이 flush()를 호출할 필요가 없었죠. flush()는 변기물을 내리는 것처럼 버퍼를 흘려보내서 비워준다는 뜻입니다.

 

혹은 이렇게 이렇게 꼭 flush()를 하기 귀찮고 자동으로 flush()해주기를 원한다면 아래의 생성자를 사용하세요.

pw=new PrintWriter(new OutputStreamWriter(s.getOutputStream()),true);
PrintWriter(OutputStream out, boolean autoFlush)
Creates a new PrintWriter from an existing OutputStream.

두번째 인자는 auto flush를 하느냐 마냐를 정해줍니다. true면 auto flush활성화하는것입니다.

 

클라이언트는 이와 반대의 순서로 메시지를 보내고, 읽어야겠죠? 

클라리언트 구현

public class TCPClient extends JFrame implements KeyListener{
	//...생략...//
	@Override
	public void keyTyped(KeyEvent e) {}

	@Override
	public void keyPressed(KeyEvent e) {}

	@Override
	public void keyReleased(KeyEvent e) {
		int keyCode = e.getKeyCode();
		if(keyCode==KeyEvent.VK_ENTER) {
			try {
				if(br==null||pw==null) return;
				String msgText=messageField.getText();
				if(msgText==null||msgText.length()==0) return;
				
				if(msgText.equals("quit")) {	//종료
					if(br!=null) br.close();
					if(pw!=null) pw.close();
					if(s!=null) s.close();
					return;
				}
				
				
				pw.println(msgText);	//1. 보내고
				pw.flush();
				messages.append(br.readLine()+"\n");	//2. 받음
				
				//뒷정리
				messageField.setText("");
				messageField.requestFocus();
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		}	
	}

 

나머지 불필요한 코드는 넣지 않았습니다. 사실 별로 어려운 소스코드는 아닙니다. 단지 소켓으로 인풋, 아웃풋 스트림을 생성한 후에 println()과 flush()로 문자열을 전송한 후에 다시 에코된 메시지를 readLine으로 읽는 겁니다.

 

이제 결과를 보시면 아래와 같이 잘 echo됨을 확인할 수 있습니다.

 

 

여기까지 Socket을 생성하여 연결하는 방법과 네트워크 전송에 사용하는 reader인 BufferedReader, writer인 PrintWriter에 대한 짤막한 사용법도 알아보았습니다.

 

이런 개념을 바탕으로 여러분들이 응용하기 나름이겠지만 채팅 어플리케이션도 만들 수 있고, 심지어 패인트 객체의 정보를 네트워크상으로 주고 받음으로써 캐치마인드까지도 구현해볼 수도 있습니다. 컴퓨터 프로그램에서 네트워크는 절대 빠질수 없는 개념이니 자바로 꼭 익혀두시길 바랍니다.

 

뭐, 시간되면 채팅 어플리케이션 만드는 포스팅도 진행해보도록 할게요. 그럴 시간이 나오려나...

반응형
블로그 이미지

REAKWON

와나진짜

,

JTable

엑셀과 같이 행과 열이 존재하여 표의 형태로 자료를 편집하고 관리할 수 있는 Swing 컴포넌트가 JTable입니다. 행과 열이 만나는 곳이 셀이라고 하며 각 행들은 보통 레코드라고 부릅니다. JTable도 JList와 같이 단일 선택 모드와 다중 선택 모드가 있는데 여기서는 단일 선택 모드를 설명하도록 하겠습니다.

다중 선택모드는 JList와 매우 흡사하므로 아래의 JList를 다루는 포스팅을 참고하시면 될겁니다.

reakwon.tistory.com/166

 

[자바/GUI] Swing JList(다중 선택, 단일 선택) - 동적으로 아이템 추가, 삭제

JList - 단일 선택 모드(SingleSelectionMode) (다중 선택 모드는 아래쪽에서 설명합니다.) Swing 컴포넌트 중에서 자료를 추가하고 삭제할 수 있도록 만든 컴포넌트가 있습니다. 그게 JList인데요. 여러분

reakwon.tistory.com

 

그래서 아래의 프로그램을 구현해보면서 JTable이 어떻게 동작하는지 확인해보도록 합시다.

JTable Example

 

위의 프로그램은 아래와 같이 동작합니다.

1. 추가 - 이름, 나이, 성별, 3개의 점수를 JTextField로 입력받아서 Table에 추가가 됩니다.

2. 삭제 - 선택된 레코드가 있다면 그 레코드를 JTable에서 삭제하며, 없으면 가장 위쪽(가장 먼저 추가된)의 레코드를 삭제합니다. 

3. 읽기 - 셀이 선택되면 시스템 Output에 단순 출력합니다.

 

구현

1. 기본 설정

우선 JFrame을 상속받아 기본 세팅을 진행해보도록 합시다. 여기서는 레이아웃의 배치와 JTable을 초기화, 각 버튼, 텍스트 필드의 이벤트 설정등을 생성자에서 구현하게 됩니다.

public class JTableTest extends JFrame implements MouseListener,KeyListener {
	
	private final String[] labels= {"Name","Age","Sex","Korean","English","Math"};
	private JTextField []fields=new JTextField[6];
	
	private JScrollPane scrolledTable;
	private JTable table;
	
	private JButton addBtn;
	private JButton delBtn;
	
	public JTableTest(String title) {
		
		
		this.setLayout(new BorderLayout(10,10));
		
		JPanel topPanel=new JPanel(new GridLayout(6,4,10,5));
		
		for(int i=0;i<6;i++) {
			topPanel.add(new JLabel(labels[i]));
			fields[i]=new JTextField(30);
			topPanel.add(fields[i]);
		}
		topPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
		this.add("North",topPanel);				//가장 위쪽 Panel 설정
		
		String header[]= {"Name","Age","Sex","Korean","English","Math"};
		DefaultTableModel model=new DefaultTableModel(header,0);	//header추가, 행은 0개 지정
		
		table=new JTable(model);
		table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
		scrolledTable=new JScrollPane(table);	//스크롤 될 수 있도록 JScrollPane 적용
		scrolledTable.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));	//너무 붙어있어서 가장자리 띄움(padding)
		this.add("Center",scrolledTable);	//가운데에 JTable 추가
		
		
		
		JPanel rightPanel=new JPanel(new GridLayout(5,1,10,10));
		
		addBtn=new JButton("레코드 추가");
		delBtn=new JButton("레코드 삭제");			
		
		
		rightPanel.add(addBtn);
		rightPanel.add(delBtn);
		rightPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
		
		this.add("East",rightPanel);		//오른쪽에 버튼들 추가
		
		
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setSize(620,400);
		this.setLocationRelativeTo(null);	//창 가운데 위치
		this.setVisible(true);
		
		
		//이벤트 추가
		addBtn.addMouseListener(this);	//추가 처리
		delBtn.addMouseListener(this);	//삭제 처리
		for(int i=0;i<6;i++)
			fields[i].addKeyListener(this);	//엔터 처리
		table.addMouseListener(this);	//셀 읽기 처리
	}


	@Override
	public void mousePressed(MouseEvent e) {}
	@Override
	public void mouseReleased(MouseEvent e) {}
	@Override
	public void mouseEntered(MouseEvent e) {}
	@Override
	public void mouseExited(MouseEvent e) {}

	//KeyListener Overrides
	@Override
	public void keyTyped(KeyEvent e) {}
	@Override
	public void keyPressed(KeyEvent e) {}
	@Override
	public void keyReleased(KeyEvent e) {
	
	}

}

 

JTable은 DefaultTableModel으로 테이블의 데이터를 추가, 삭제할 수 있습니다. 그래서 DefaultTableModel을 생성한 다음 JTable 생성자로 넘겨줍니다. DefaultTableModel의 생성자는 여러가지가 있는데, 여기서는 header만 있고 맨 처음 상태에서는 아무런 레코드가 없는 상태이기 때문에 row count=0으로 지정하여 생성합니다.

그리고 JTable이 Record가 넘쳐날 경우 잘리지 않게 JScrollPane으로 스크롤 가능하게 만들어준 점도 주목해주세요.

2. 추가 구현 - addRecord()

조금 전 DefaultTableModel을 통해서 레코드를 추가하는 것이 가능합니다. 우선 JTable에 아까 전달한 DefaultTableModel을 얻어온 다음 addRow() 메소드를 통해 추가가 가능합니다. addRow()는 Object 배열을 받을 수 있도록 구현이 되어있는데, 우리는 입력받는 TextField의 텍스트를 String 배열로 넘겨줄 것입니다.

public void addRecord() {
	DefaultTableModel model=(DefaultTableModel)table.getModel();
	String []record=new String[6];
	for(int i=0;i<6;i++) {
		if(isInvalidInput(fields[i].getText())) {
			System.out.println("Invalid Input");
			return;
		}
		record[i]=fields[i].getText();
	}
	model.addRow(record);
}

 

여기서 유요한 입력인지 아닌지를 판별해주는 것이 isInvalidInput()인데, 간단하게 텍스트 필드가 비어있는지 확인하는 메소드입니다.

	private boolean isInvalidInput(String input) {
		return input==null||input.length()==0;
	}

 

그리고 다음 입력을 편하게 받을 수 있도록 필드를 전부 비워주고 가장 맨 처음인 이름 필드에 focus를 하도록 하지요. model.addRow(record) 밑에 아래의 라인을 추가 구현하면 됩니다.

	//모든 TextField 비우기
	for(int i=0;i<6;i++)
		fields[i].setText("");
		
	fields[0].requestFocus();

 

이제 addRecords()라는 레코드 추가 메소드는 구현이 되었습니다. 이 메소드는 추가버튼이나 텍스트 필드에서 엔터가 쳐지는 순간 호출하면 됩니다.

	@Override
	public void mouseClicked(MouseEvent e) {
		Object src=e.getSource();
		if(src==addBtn) {
			addRecord();
		}

	}

 

	@Override
	public void keyReleased(KeyEvent e) {
		int keyCode = e.getKeyCode();
		if(keyCode==KeyEvent.VK_ENTER) {
			addRecord();
		}
	}

 

3. 삭제 구현 - removeRecord()

우리는 삭제할때 선택된 레코드가 있으면 그 레코드를 삭제하고 아니면 가장 처음 추가된 레코드를 삭제한다고 했죠. 우리가 구현한 removeRecord()는 선택된 index를 넘겨받는데 만약 선택된 index가 없다면 index는 -1의 값을 가진다고 합시다.

	public void removeRecord(int index) {
		DefaultTableModel model=(DefaultTableModel)table.getModel();
		if(index<0) {
			if(table.getRowCount()==0)//비어있는 테이블이면
				return;
			index=0;
		}
		model.removeRow(index);
	}

 

index가 -1인 경우에는 아예 Table에 레코드가 없거나, 레코드는 있는데 선택하지 않는 경우입니다. 레코드의 수를 확인하려면 JTable의 getRowCount() 메소드를 통해서 확인하면 됩니다. 

getRowCount() == 0 : 이미 비어있으니 아무 동작하지 않고 종료합니다.

getRowCount() > 0 : 레코드가 존재하니까 가장 상위 레코드인 0을 index의 값으로 저장합니다.

이제 DefaultTableMode의 removeRow()에 그 index를 넘겨주면 해당 index의 행이 삭제됩니다. 이 removeRecords() 메소드는 삭제버튼이 눌리게 되면 호출하면 됩니다.

	@Override
	public void mouseClicked(MouseEvent e) {
		Object src=e.getSource();
		//...생략 ...//
		if(src==delBtn) {
			int selected=table.getSelectedRow();
			removeRecord(selected);
		}
	}

 

JTable의 getSelectedRow() 메소드는 선택된 레코드가 없다면 -1을 넘겨줍니다. 그래서 우리는 그대로 removeRecords()에 그 값을 넘겨주면 됩니다.

 

4. 읽기 - printCell()

읽으려면 cell의 위치를 알아야합니다. 그래서 선택된 행의 위치, 선택된 열의 위치를 얻어와서 DefaultTableModel의 getValueAt()에 전달해주면 셀을 읽어올 수 있습니다. 내용을 출력해주는 메소드를 printCell()이라고 하여 정의하면 이렇습니다.

	public void printCell(int row,int col) {
		DefaultTableModel model=(DefaultTableModel)table.getModel();
		System.out.println(model.getValueAt(row, col));
	}

 

이 메소드는 JTable이 클릭할때 발생시켜주면 되는데 이때 클릭된 셀의 행과 열 위치를 JList의 getSelectedRow()메소드와 getSelectedColumn() 메소드를 통해서 알아오면 됩니다.

	@Override
	public void mouseClicked(MouseEvent e) {
		Object src=e.getSource();
		
		//... 생략 ...//
		if(src==table) {
			int row=table.getSelectedRow();	
			int col=table.getSelectedColumn();
			
			printCell(row,col);
		}
		
	}

 

전반적인 소스 구현은 끝이 났습니다.

 

전체 코드

전체 코드는 아래와 같습니다.

 

- JTableTest.java


public class JTableTest extends JFrame implements MouseListener,KeyListener {
	
	private final String[] labels= {"Name","Age","Sex","Korean","English","Math"};
	private JTextField []fields=new JTextField[6];
	
	private JScrollPane scrolledTable;
	private JTable table;
	
	private JButton addBtn;
	private JButton delBtn;
	
	public JTableTest(String title) {
		
		
		this.setLayout(new BorderLayout(10,10));
		
		JPanel topPanel=new JPanel(new GridLayout(6,4,10,5));
		
		for(int i=0;i<6;i++) {
			topPanel.add(new JLabel(labels[i]));
			fields[i]=new JTextField(30);
			topPanel.add(fields[i]);
		}
		topPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
		this.add("North",topPanel);				//가장 위쪽 Panel 설정
		
		String header[]= {"Name","Age","Sex","Korean","English","Math"};
		DefaultTableModel model=new DefaultTableModel(header,0);	//header추가, 행은 0개 지정
		
		table=new JTable(model);
		scrolledTable=new JScrollPane(table);	//스크롤 될 수 있도록 JScrollPane 적용
		scrolledTable.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));	//너무 붙어있어서 가장자리 띄움(padding)
		this.add("Center",scrolledTable);	//가운데에 JTable 추가
		
		
		
		JPanel rightPanel=new JPanel(new GridLayout(5,1,10,10));
		
		addBtn=new JButton("레코드 추가");
		delBtn=new JButton("레코드 삭제");			
		
		
		rightPanel.add(addBtn);
		rightPanel.add(delBtn);
		rightPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
		
		this.add("East",rightPanel);		//오른쪽에 버튼들 추가
		
		
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setSize(620,400);
		this.setLocationRelativeTo(null);	//창 가운데 위치
		this.setVisible(true);
		
		
		//이벤트 추가
		addBtn.addMouseListener(this);	//추가 처리
		delBtn.addMouseListener(this);	//삭제 처리
		for(int i=0;i<6;i++)	//엔터 처리
			fields[i].addKeyListener(this);
		table.addMouseListener(this);	//셀 읽기 처리
		
	}

	private boolean isInvalidInput(String input) {
		return input==null||input.length()==0;
	}

	public void removeRecord(int index) {
		DefaultTableModel model=(DefaultTableModel)table.getModel();
		if(index<0) {
			if(table.getRowCount()==0)//비어있는 테이블이면
				return;
			index=0;
		}
		model.removeRow(index);
	}
	public void addRecord() {
		DefaultTableModel model=(DefaultTableModel)table.getModel();
		String []record=new String[6];
		for(int i=0;i<6;i++) {
			if(isInvalidInput(fields[i].getText())) {
				System.out.println("Invalid Input");
				return;
			}
			record[i]=fields[i].getText();
		}
		model.addRow(record);
		
		//모든 TextField 비우기
		for(int i=0;i<6;i++)
			fields[i].setText("");
		
		fields[0].requestFocus();
	}
	
	public void printCell(int row,int col) {
		DefaultTableModel model=(DefaultTableModel)table.getModel();
		System.out.println(model.getValueAt(row, col));
	}

	//MouseListener Overrides
	@Override
	public void mouseClicked(MouseEvent e) {
		Object src=e.getSource();
		if(src==addBtn) 
			addRecord();
		
		if(src==delBtn) {
			int selected=table.getSelectedRow();
			removeRecord(selected);
		}
		
		if(src==table) {
			int row=table.getSelectedRow();	
			int col=table.getSelectedColumn();
			printCell(row,col);
		}
		
	}
	
	@Override
	public void mousePressed(MouseEvent e) {}
	@Override
	public void mouseReleased(MouseEvent e) {}
	@Override
	public void mouseEntered(MouseEvent e) {}
	@Override
	public void mouseExited(MouseEvent e) {}

	//KeyListener Overrides
	@Override
	public void keyTyped(KeyEvent e) {}
	@Override
	public void keyPressed(KeyEvent e) {}
	@Override
	public void keyReleased(KeyEvent e) {
		int keyCode = e.getKeyCode();
		if(keyCode==KeyEvent.VK_ENTER) {
			addRecord();
		}
	}
}

 

이 클래스를 main 함수에서 호출하면 됩니다.

 

 

- Main.java

public class Main {
	public static void main(String[] ar) {
		JTableTest test=new JTableTest("reakwon");
	}
}

JTable read value

여기까지 JTable에 대한 기본적인 행 추가, 삭제에 대해서 알아보았습니다. 기본적인 사용법만 읽힌다면 어렵지 않게 사용할 수 있을 것 같습니다.

 

반응형
블로그 이미지

REAKWON

와나진짜

,