{{ label!='' ? 'Label : ' : (q!='' ? '검색 : ' : '전체 게시글') }} {{ label }} {{ q }} {{ ('('+(pubs|date:'yyyy-MM')+')') }}

[Java] 자바로 프로그래밍 입문하기: 3.1. 자료형 (3)

 문자열 처리(String processing)

  여러분들은 첫 자바 프로그램에서 문자열을 사용했습니다. 자바의 String 자료형은 문자열에 대한 수많은 연산들을 포함하고 있습니다. 문자열 처리는 수많은 응용프로그램에서 사용되기 때문에 아주 중요합니다. 프로그램은 모두 문자열로 이루어졌죠. 사람들은 이메일, 블로그, 채팅 프로그램에서 문자열을 사용하며, 수많은 문서들 역시 문자열입니다.

  String 값은 일련의 char 값들입니다. 우리가 자주 쓰게 될 String 자바 API를 요약해서 소개해볼까 합니다. 

String 자료형의 API 일부


  자바는 문자열을 특별히 취급합니다. 생성자 없이 초기화할 수 있고, 문자열 리터럴을 사용할 수 있으며, concat() 메소드를 호출하는 대신 + 연산자를 이용할 수도 있습니다. 

  String 값이 char 배열과 동일한 것은 아닙니다. 하지만 아주 유사하여 입문자들에게 혼란을 일으키기도 합니다. 

  split()은 아주 강력한 메소드입니다. 정규 표현식(regular expression)에서 구분자로 활용할 수 있기 때문입니다. 예를 들어, "\\s+"는 "하나 이상의 공백 문자"를 의미합니다. split()을 이용하여, 공백으로 구분된 각 단어들을 배열에 나눠 담을 수 있죠.

문자열 연산의 예


  왜 우리가 char 배열을 사용하지 않고 String을 사용해야 할까요? 우리가 문자열을 처리할 때, 우리는 배열이 아닌 문자열 그 자체에 대해 연산을 하고 싶기 때문입니다. 이 의미를 잘 생각해보세요.

전형적인 문자열 처리 코드


문자열 처리 프로그램: genomics

  여러분들이 문자열 처리를 경험할 수 있는 예제를 소개합니다. 유전학(genomics)의 짧은 개요를 설명드릴 것이며, 유전자 발견(gene finding)으로도 알려진 간단한 문제를 해결해볼 것입니다. 유전학은 문자열 처리 응용프로그램과 꽤나 밀접합니다.

  생물학자들은 DNA 염기 서열을 A, C, T, G로 표현한 모델을 사용합니다. 각 생명체는 이러한 염기들이 모여 긴 순서를 이루고, 이것이 바로 게놈(genome)입니다. 과학자들은 게놈을 이해하는 것이 곧 생물체를 이해하는 핵심이라고 말합니다. 사람의 게놈은 30억 여개의 문자로 이루어져있죠. 웹에서 이러한 서열들을 충분히 찾아볼 수 있을 것입니다.


유전자 발견(Gene finding)

  유전자는 게놈 문자열의 부분열입니다. 게놈을 생명 활동의 기능적 단위로 나눈 것이죠. 유전자의 길이는 다양하며, 수 역시 게놈에 따라 천차만별입니다. 유전자는 일련의 코돈(codon)이며, 각 코돈은 아미노 산을 표현하는 트리플렛 코드(nucleotide triplet)입니다. 개시 코돈(start codon) ATG는 유전자의 시작을 표시하며, 종결 코돈(stop codon) TAG, TAA, TGA는 유전자의 끝을 표시합니다. 

  게놈을 분석하기 위해선 먼저 각 유전자를 식별해야 합니다. 여러분도 슬슬 감이 오시겠죠? 이는 곧 문자열 처리 문제입니다. <GeneFind>는 이러한 일을 해내는 자바 프로그램입니다. 이게 어떻게 작동하는지 알아보기 위해, 진짜 게놈은 아니지만 간단한 예시를 통해 이해해봅시다. 개시 코돈 ATG, 종결 코돈 TAG를 다음 유전자에서 식별해보죠.

ATAGATGCATAGCGCATAGCTAGATGTGCTAGC


프로그램 3.1.8: Finding genes in a genome

public class GeneFind {
	public static void main(String[] args) {
		// Use start and stop to find genes in genome. stop
		String start = args[0];
		String stop = args[1];
		String genome = StdIn.readAll();
		int beg = -1;
		for (int i = 0; i < genome.length() - 2; i++) {
		 	// Check next codon for start or stop.
			String codon = genome.substring(i, i + 3);
			if (codon.equals(start))
				beg = i;
			if ((codon.equals(stop)) && beg != -1) {
			 	// Check putative gene alignment.
				String gene = genome.substring(beg + 3, i);
				if (gene.length() % 3 == 0) {
				 	// Print and restart.
					StdOut.println(gene);
					beg = -1;
				}
			}
		}
	}
}
% more genomeTiny.txt
ATAGATGCATAGCGCATAGCTAGATGTGCTAGC

% java GeneFind ATG TAG < genomeTiny.txt
CATAGCGCA
TGC

% java GeneFind ATG TAG < genomeVirus.txt
CGCCTGCGTCTGTAC
TCGAGCGGATCGCTCACAACCAGTCGG
...
AGATTATCAAAAAGGATCTTCACC


  변수 beg은 개시 코돈을 직면했을 때의 번호를 저장합니다. -1일 경우에는 의미있는 유전자가 아님을 의미하죠. 종결 코돈을 만났을 때 beg 값이 -1이라면, 무시합니다. 9번에 나타나는 TAG는 무시되는데, 만약 이것이 종결 코돈이라면 염기 서열이 3의 배수로 이루어지지 않기 때문입니다. 이런 식으로 종결 코돈을 찾고 적합한 종결 코돈이라고 판단이 되면 유전자를 출력합니다.


입출력 되새기기

  여러분들은 숫자와 글자, 그림 등을 출력하는 방법에 대해 1.5절에서 알아봤었습니다. 프로그램의 안팎으로 정보를 가져오는 메커니즘이 얼마나 유용한지 느낄 수 있었죠. "표준"이라는 약속이 프로그램을 어느 곳이든 접근할 수 있게 만들어줬기 때문입니다. 단점도 있습니다. 운영체제의 파이프, 리디렉션 메커니즘에 의존하게 된다는 것이죠. 객체지향 프로그래밍을 이용하면, 여러 개의 입력 스트림과 출력 스트림을 한 프로그램이 받아들일 수 있게 만들 수 있습니다. 

자바 프로그램의 조감도

  특히, 우리는 자료형 In, Out, Draw를 각각 입력 스트림, 출력 스트림, 그림으로 정의하였습니다. In.java, Out.java, Draw.java는 여러분이 Std 라이브러리를 설치할 때 함께 설치되었을 것입니다.

  이 자료형들을 통해, 수많은 자료 처리 작업을 유연하게 해낼 수 있습니다. 각각 하나의 입력 스트림과 출력 스트림, 그림을 이용하는 것보단 당연히 낫겠죠. 다양한 객체들을 각 자료형에 맞게 정의하고, 각 스트림에 연결하여 가공해낼 수 있습니다. 객체들을 변수에 할당하고, 인자로 전달하고, 값으로 반환하고, 배열을 생성하는 등의 작업에서 훨씬 유연하게 대처할 수 있습니다.


입력 스트림 자료형

  In은 StdIn의 좀 더 일반적인 버전입니다. 숫자와 글자를 읽고, 파일, 웹사이트, 표준 입력 역시 제공하죠. 이는 입력 스트림 자료형을 구현합니다. 다음 API들이죠.

입력 스트림을 구현하는 자료형의 API

  하나의 추상 입력 스트림(표준 입력)에 제한받는 것 대신, 입력 스트림의 자료를 직접 지정할 수 있습니다. 그 자료는 심지어 파일이나 웹사이트가 될 수도 있습니다. String 인자를 갖는 생성자를 호출하면, In은 먼저 여러분의 컴퓨터에 해당 이름을 가진 파일이 있는지 찾아볼 것입니다. 만약 존재하지 않는다면, 웹사이트의 주소라 판단하고 웹사이트에 연결을 시도하죠. 

  두 경우 입력 스트림의 자료가 되며, read*() 메소드를 이용하여 값을 읽어들일 수 있습니다. 이러한 변형은 같은 프로그램에서 여러 파일에 접근할 수 있게 만듭니다. 이전에는 파이프나 리디렉션을 통해 단 하나의 파일 혹은 입력 스트림으로만 접근할 수 있었죠. 

  웹과 파일 접근을 통해 수많은 정보들을 다룰 수 있게 됩니다. 과학자들은 게놈의 범위, 단백질의 서열, 위성 사진, 천문 관측과 관련된 수많은 실험들의 결과값 및 측정값들을 주기적으로 게시합니다. 경제 서비스 회사, 주식 거래 등은 웹을 통해 세부 정보들을 얻어낼 수 있죠. 정부의 선거 결과 역시 얻어낼 수 있겠네요. 


출력 스트림 자료형

  In과 비슷하게, Out은 StdOut의 좀 더 일반적인 버전입니다. 다양한 출력 스트림에 글자들을 작성할 수 있습니다. API는 StdOut과 동일한 부분도 존재합니다. 생성자의 인자로 출력할 파일의 이름을 지정할 수 있습니다. 생성자의 인자로 아무것도 넘겨주지 않는다면, 표준 출력과 동일합니다.

출력 스트림을 구현하는 자료형의 API


파일 연결과 필터링

  <프로그램 3.1.9>는 In과 Out을 사용하는 클라이언트 예제입니다. 다수의 입력 스트림을 이용하여 하나의 파일로 합쳐주죠. 몇몇 운영체제는 cat이라는 명령어로 이러한 함수를 구현합니다. 하지만 우리는 우리 입맛에 맞게 프로그램을 고칠 수 있다는 점에서 더 유용하죠. filter로 활용하는 것입니다. 부적절한 정보를 무시하고, 형식을 바꾸고, 몇몇 자료만 선택하는 등 활용할 방법은 무궁무진하죠.


프로그램 3.1.9: Concatenating files

public class Cat {
	public static void main(String[] args) {
	 	// Copy input files to out (last argument).
		Out out = new Out(args[args.length - 1]);
		for (int i = 0; i < args.length - 1; i++) {
		 	// Copy input file named on ith arg to out.
			In in = new In(args[i]);
			String s = in.readAll();
			out.println(s);
		}
	}
}
% more in1.txt
This is

% more in2.txt
a tiny
test.

% java Cat in1.txt in2.txt out.txt

% more out.txt
This is
a tiny
test.


스크린 스크래핑(Screen scraping)

  In과 String의 조합을 이용해 모든 웹의 자료에 접근할 수 있습니다. 브라우저나 운영체제의 도움 없이 말이죠! 스크린 스크래핑이라는 기법이 있습니다. 이것의 목적은 웹 페이지에서 특정한 정보만을 추출해내는 것입니다. 다행히도 웹 페이지는 이진법의 숫자 대신, 고도화된 형식의 텍스트 파일로 정의되어 있습니다. 웹 브라우저는 이러한 것들을 다시 여러분들이 볼 수 있게 표현해주는 것이죠. 

  또한 브라우저는 웹 페이지를 생성하는 소스 코드에 관여할 수 있는 메커니즘이 존재합니다. 예를 들어, 여러분은 goog라는 심볼을 지닌 회사의 주가를 확인하기 위해서 다음 주소에 접근해볼 수 있습니다. http://finance.yahoo.com/q?s=goog. 곧 이는 인자 goog를 넘겨주는 것과 유사합니다. 이를 이용해서 어떤 회사든 주가 정보를 확인할 수 있을 것입니다.

  또한 웹은 특정한 텍스트 파일을 참조하고 있습니다. 이는 마크업 언어라고 하는, 특정 구조를 표현하기 위해 작성된 파일입니다. 바로 HTML이죠. 자바 프로그램의 관점에서 보면, 이는 In 입력 스트림으로 접근할 수 있는 단순한 String 값에 지나지 않죠. 여러분은 다음을 통해 특정 웹사이트의 HTML 코드를 다운받을 수 있습니다.

java Cat "http://finance.yahoo.com/q?s=goog" mycopy.txt

  자, 이제 goog가 $2273.3에 거래되고 있다는 걸 확인해봅시다. "2273.3"을 해당 페이지의 소스에서 검색하면, 여러분은 HTML 코드에 둘러싸인 주가를 확인할 수 있을 것입니다. 주가는 <b>와 </b>라는 태그로 둘러싸여있고, "Last Trade:" 뒤에 위치하고 있네요.

  String 자료형의 indexOf(), substring() 메소드만 있으면 이러한 정보들을 쉽게 얻어낼 수 있습니다. 다음에 나올 프로그램인 <StcokQuote>를 참고하세요. 이 프로그램은 http://finance.yahoo.com의 형식에 의존해 동작합니다. 다른 웹사이트에서 동일한 HTML 형식을 사용할 리는 없으므로, 이것이 변한다면 제대로 동작하지 않겠죠. 

  어쨌거나, 우리가 웹사이트의 정보를 직접 얻어내고 다룰 수 있다는 사실은 굉장한 가능성을 열어줍니다. 꼭 주식에만 한정된 이야기는 아니겠죠. 다양한 정보들을 찾아서 여러분만의 방식으로 다루고, 그것을 더 효율적으로 활용할 수 있는 방법이 생긴 것입니다.


프로그램 3.1.10: Screen scraping for stock quotes

public class StockQuote {
	public static double price(String symbol) {
	 	// Return current stock price for symbol.
		In page = new In("https://finance.yahoo.com/quote/" + symbol);
		String in = page.readAll();
		int trade = in.indexOf("Trsdu(0.3s)", 0);
		int from = in.indexOf(">", trade);
		int to = in.indexOf("</span>", from);
		String price = in.substring(from + 1, to);
		price = price.replace(",", "");
		return Double.parseDouble(price);
	}

	public static void main(String[] args) {
		StdOut.println(price(args[0]));
	}
}
% java StockQuote goog
2371.11
% java StockQuote bb
8.12

(기존의 <StockQuote>는 Yahoo 웹사이트의 변화로 인해 잘 작동하지 않으며, 잘 작동할 수 있게끔 역자가 수정한 코드를 대신 삽입하였습니다)


자료 추출

  다중 입출력 스트림을 유지함으로써 수없이 다양하고 많은 양의 자료들을 유연하게 처리할 수 있습니다. 또 다른 예제를 살펴보죠. 과학자나 애널리스트들이 스프레드시트 프로그램의 자료를 다룬다고 생각해보세요. 

  이러한 스프레드시트의 표는 보통 행이 많고 열은 적습니다. 또한 모든 자료들을 원하진 않고, 특정 열에 대해서만 관심이 있을 것입니다. 이러한 정보 처리를 스프레드시트 프로그램으로 처리해도 좋지만, 여러분이 직접 만든 프로그램보다 유연하진 않을 것입니다.

  이러한 문제를 해결하기 위해서, 먼저 자료를 텍스트 파일로 만들어야 합니다. 특수문자로 각 열을 구분하게 만들고, 그것을 입력 스트림으로 읽어들이면 되죠. 보통은 쉼표를 구분자로 사용합니다. 한 줄에 한 행씩, 각 열은 쉼표로 구분하는 것이죠. 이러한 파일들을 comma-separated-value, .csv 파일이라고 합니다.

  split() 메소드만 있다면, 이러한 파일들을 쉽게 읽어들일 수 있습니다. 다음 <Split> 프로그램을 참고하세요.


프로그램 3.1.11: Splitting a file

public class Split {
	public static void main(String[] args) {
	 	// Split file by column into N files.
		String name = args[0];
		int N = Integer.parseInt(args[1]);
		String delim = ",";
		// Create output streams.
		Out[] out = new Out[N];
		for (int i = 0; i < N; i++)
			out[i] = new Out(name + i + ".txt");
		In in = new In(name + ".csv");
		while (!in.isEmpty()) {
		 	// Read a line and write fields to output streams.
			String line = in.readLine();
			String[] fields = line.split(delim);
			for (int i = 0; i < N; i++)
				out[i].println(fields[i]);
		}
	}
}
% more DJIA.csv
...
31-Oct-29,264.97,7150000,273.51
30-Oct-29,230.98,10730000,258.47
29-0ct-29,252.38,16410000,230.07
28-0ct-29,295.18,9210000,260.64
25-Oct-29,299.47,5920000,301.22
24-0ct-29,305.85,12900000,299.47
23-Oct-29,326.51,6370000,305.85
...

% java Split DJIA 3

% more DJIA2.txt
...
7150000
10730000
16410000
9210000
5920000
12900000
6370000
...


계속.

댓글

이 블로그의 인기 게시물

[코딩의탑] 4층: 툰 쉐이딩

[코딩의탑] 3층: 바다 렌더링

[코딩의탑] 5층: 포탈(Portal), 더 나아가기