자바 객체와 XML간의 변환에관해서는 몇 가지 라이브러리들이 존재하고 있습니다만 이번 시간에는 Java SDK에 기본으로 포함되어 있는 java.beans.XMLDecoder와 java.beans.XMLEncoder를 이용하여 자바객체<->XML간 변환 방법을 알아보겠습니다.
java.beans.XMLDecoder와 java.beans.XMLEncoder클래스는 J2SE 1.4 버전부터 이용할 수 있습니다.
//자.. 객체를 XML로 변환 시킵니다. XMLEncoder encoder = new XMLEncoder( new BufferedOutputStream( new FileOutputStream("C:\\Sample.xml"))); encoder.writeObject(sample); encoder.close(); //객체 레퍼런스를 찍어보구요.. System.out.println(sample);
//이젠 XML에서 객체로 복원시켜 봅니다. XMLDecoder decoder = new XMLDecoder( new BufferedInputStream( new FileInputStream("C:\\Sample.xml"))); SampleBean sample2 = (SampleBean) decoder.readObject(); decoder.close(); //객체 레퍼런스를 찍어보구요.. System.out.println(sample2); } }
예.. 변환의 핵심은 XMLEncoder 클래스의 writeObject() 메소드와 XMLDecoder 클래스의 readObject() 메소드 에 있습니다.
자바에는 객체 직렬화(Serialize) 란게 있지요.. 위 XMLDecoder와 XMLEncoder가 직렬화 기능을 이용하는건지 아닌지 확인 해 볼까요? 가장 정확한 방법은 XMLDecoder와 XMLEncoder의 소스를 보는것이겠지만 이미 위 예제 소스만으로도 충분히 예상 하실 수 있습니다. 위 예제의 SampleBean 이 Serializable 하지 않음에도 xml문서로 생성되었습니다. 예.. XMLDecoder와 XMLEncoder 내부적으로 java Reflection을 이용하여 xml, 객체간 변환을 수행하고 있습니다.
java Reflection을 이용한다고 했습니다. 이는 getXXX, setXXX 메소드가 없다면 해당 변수를 xml로 만들어내지 못하고 객체로 복원해 내지도 못함을 의미합니다.
또 하나, XMLEncoder를 통해 변환된 xml은 문서의 형태로 보아도 외부 시스템과의 정보의 교환보다는 객체의 상태저장/복원을 목적으로 사용하는게 더 어울릴거란 생각이 드네요.
어떤가, 무척 간단하지 않는가? 그럼, 한걸음 더 나아가 보자. 윈도우는 파일확장자별로 대표 프로그램을 등록하여 파일을 더블클릭하는것만으로 해당 프로그램을 실행 할 수가 있는데 이렇게 특정 파일을 선택하여 그와 연결된 프로그램을 실행하는 코드도 자바로 가능 할까? 물론, 가능하다. 여기에는 윈도우즈 명령어를 조금 알아야 하는 부분이 있지만 그 부분만 알고 있다면 나머지는 위와 동일하게 Runtime 객체로 그 명령어를 실행 하기만 하면된다. 아래 코드를 보자.
package javacodesnipet;
import java.io.File; import java.io.IOException;
/** * Runtime클래스를 이용하여 해당 파일과 연결된 프로그램을 실행한다. * @author 신윤섭 */ public class RuntimeExample { public static void main(String[] args){ try { File file = new File("d:\\sample.pdf");
//MS Windows Only Process p= Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + file.getAbsolutePath()); // or //Process p= Runtime.getRuntime().exec("rundll32 SHELL32.DLL,ShellExec_RunDLL " + // file.getAbsolutePath());
//Apple Mac Only //Process p= Runtime.getRuntime().exec("open " + file.getAbsolutePath());
위 코드는 윈도우 탐색기에서 d:\sample.pdf를 더블클릭하여 아크로뱃리더를 실행하고 해당 pdf문서를 여는것과 동일한 효과를 얻을 수 있다. 윈도우의 rundll32 명령으로 해당 파일과 연결된 프로그램을 구동 할 수 있는데 이를 Runtime객체를 이용하여 수행하는 코드이다.
double을 퍼센트(%)로 표현하기위해 여러가지 방법을 이용할 수 있지만 java.text 패키지의 NumberFormat 클래스 API를 이용하면 아래와 같이 간단히 double값을 퍼센트 문자열로 변환할 수 있다.
package javacodesnipet;
import java.util.Locale; /** * * @author 신윤섭 */ public class NumberFormatPercent { public static void main(String[] args){ //아래 double 값을 퍼센트 문자열로 표현해 보자 double value = 0.343234532d; //NumberFormat 객체로 부터 PercentInstance를 얻어온다. java.text.NumberFormat pformat = java.text.NumberFormat.getPercentInstance(Locale.KOREA); //퍼센트로 표현할 소수점 이하의 자리수를 정한다. pformat.setMaximumFractionDigits ( 4 ); //자... 이제 double을 String형식의 퍼센트로 변환하자. String sPercent = pformat.format ( value); //값을 확인 해 보자 -> 34.3235% 과 같이 표현 된다. System.out.println(sPercent); } }
우리는 java.util.Date 혹은 java.sql.Date 타입의 객체에서 특정한 형식의 문자열로 날짜를 가져오기 위해서 java.text.SimpleDateFormat 클래스를 이용할 수 있다는 것을 알고 있다.
package javacodesnipet; /** * @author 신윤섭 */ public class SimpleDateFormatTest {
public static void main(String[] args){
// 현재 일시 정보를 갖는 Date 객체를 생성한다. java.util.Date currentDate = new java.util.Date(); //Date객체로부터 특정한 형식의 문자열로 일시를 만들어내기 위한 포매터를 생성한다. java.text.SimpleDateFormat format = new java.text.SimpleDateFormat("yyyy년MM월dd일 HH시mm분ss초"); //포매터를 이용하여 Date객체로부터 문자열을 만들어낸다. String dateString = format.format(currentDate); //변환된 문자열을 확인한다. 결과 : 2007년07월22일 02시21분42초 System.out.println(dateString);
} }
위의 코드는 흔히 사용하는 코드일 것이다.
그런데 이 SimpleDateFormat 클래스의 parse() 메소드를 이용하면 역으로 문자열 형식의 날짜로부터 Date객체를 생성 해낼 수도 있다.
package javacodesnipet; /** * @author 신윤섭 */ public class SimpleDateFormatTest {
public static void main(String[] args){ try { // "2007-07-22" 이란 문자열로 2007년 7월 22일의 정보를 갖는 Date객체를 만들어보자 String textDate = "2007-07-22";
// 입력할 날짜의 문자열이 yyyy-MM-dd 형식이므로 해당 형식으로 포매터를 생성한다. java.text.SimpleDateFormat format = new java.text.SimpleDateFormat("yyyy-MM-dd");
//SimpleDateFormat.parse()메소드를 통해 Date객체를 생성한다. //SimpleDateFormat.parse()메소드는 입력한 문자열 형식의 날짜가 //포맷과 다를경우 java.text.ParseException을 발생한다. java.util.Date date = format.parse(textDate);
//위에서 만든 date객체가 정말 7월22일인지 확인 해보자. java.text.SimpleDateFormat format1 = new java.text.SimpleDateFormat("yyyy년MM월dd일 HH시mm분ss초"); String dateString = format1.format(date); //Date객체의 날자를 확인한다.. 결과 : 2007년07월22일 00시00분00초 System.out.println(dateString); } catch (java.text.ParseException ex) { ex.printStackTrace(); }
/** * Stream을 이용한 파일복사 코드 스니핏 * @author 신윤섭 */ public class StreamCopy {
/** * source에서 target으로의 파일 복사 * @param source * @param target */ public void copy(String source, String target) { //복사 대상이 되는 파일 생성 File sourceFile = new File( source );
/** * Buffer를 이용한 파일복사 코드 스니핏 * @author 신윤섭 */ public class BufferCopy {
/** * source에서 target으로의 파일 복사 * @param source * @param target */ public void copy(String source, String target) { //복사 대상이 되는 파일 생성 File sourceFile = new File( source );
try { //스트림 생성 inputStream = new FileInputStream(sourceFile); outputStream = new FileOutputStream(target); //버퍼 생성 bin = new BufferedInputStream(inputStream); bout = new BufferedOutputStream(outputStream);
//버퍼를 통한 스트림 쓰기 int bytesRead = 0; byte[] buffer = new byte[1024]; while ((bytesRead = bin.read(buffer, 0, 1024)) != -1) { bout.write(buffer, 0, bytesRead); }
/** * NIO Channel을 이용한 파일복사 코드 스니핏 * @author 신윤섭 */ public class ChannelCopy {
/** * source에서 target으로의 파일 복사 * @param source 복사할 파일명을 포함한 절대 경로 * @param target 복사될 파일명을 포함한 절대경로 */ public void copy(String source, String target) { //복사 대상이 되는 파일 생성 File sourceFile = new File( source );
try { //스트림 생성 inputStream = new FileInputStream(sourceFile); outputStream = new FileOutputStream(target); //채널 생성 fcin = inputStream.getChannel(); fcout = outputStream.getChannel();
//채널을 통한 스트림 전송 long size = fcin.size(); fcin.transferTo(0, size, fcout);
이상의 샘플코드를 이용하여 700Mbytes 짜리 파일을 5번 복사하여 그 시간을 측정해 본 결과는 아래와 같다. 테스트는 Win XP Pro sp3, Intel Core2 Duo 2GHz, 2Gbytes, 5400rpm의 노트북용 HDD 에서 JDK 1.6.0_06 을 이용하여 이루어졌다.
결과는 아래와 같다.
Stream을 이용한 파일 복사
Buffer를 이용한 파일 복사
Channel을 이용한 파일 복사
프로파일러를 이용하여 측정한 값이기 때문에 프로파일러의 처리량 만큼의 차이는 있겠지만 상대적 성능 비교 에는 문제가 없으리라 생각 된다. 실측에서도 예상대로 Stream , Buffer, Channel 순으로 파일 복사 시간이 줄어들고 있음을 볼 수있다.
이번에는 조금 더 들여다 보기로 하자. 700m짜리 파일을 한번 복사하는데 어떤 클래스와 메소들이 참여하고 있는지, 그리고 메소드가 몇번이나 호출되고 있는지 확인 해 보는것도 재미있을 것이다.
FileInputStream.read()실행에 대부분의 시간을 소비하고 있다.
스트림을 이용한 파일복사이다. FileInputStream.read()메소드가 71만여번 호출되고있으며 실행시간의 대부분을 이 메소드를 실행 하는데 소비하고 있음을 알 수 있다.
Stream보다는 나아졌다고는 하나 역시 read()메소드가 대부분의 실행시간을 소비하고 있다.
Buffer를 이용한 방법. 위의 Stream을 이용한 방법에 비해 수행시간이 약간은 줄어들었지만 이는 Buffer를 활용 함으로써 FileOutputStream.write() 수행시간을 줄인데 따른 성능 향상이며, FileInputStream.read()메소드는 약 9만번 호출되고 있다.
위 두 방식과는 확연히 다른 동작 성향을 보여주고 있다.
마지막으로 채널을 이용한 파일복사의 경우 위 두 경우와 비교하여 호출되는 메소드나 호출횟수등 전혀 다른 동작 성향을 보이고 있다. read 도 하지 않은채 FileDispatcher.write() 메소드를 단 한번 호출 하는것으로 파일 복사를 끝내고 있다. 이 FileDispatcher.write() 하부구조에서는 OS의 네이티브IO를 호출하고 있으리라 미루어 짐작할 수 있다.
이상으로 파일복사(스트림전송)의 세가지 방식과 그 성능에 대해 간략하게 알아보았다. 위 실험 결과는 크기가 비교적 큰 파일의 복사에서 나타나는 성향며, 다수의 작은 크기의 파일을 복사한다면 그 결과가 달라질 수도 있음을 밝혀둔다.
io작업이 필요한데 JDK 1.4 이상의 버전을 이용할 수 있다면 나은 성능을 보장하는 nio를 사용하지 않을 이유가 없어보인다.
아 그리고 BufferedInputStream 생성자의 인자로 넘겨준 InputStream은 BufferedInputStream의 close()가 호출 될 때
같이 close 됩니다. Channel 의 경우도 Lazy pattern 으로 생성되었다가 InputStream이 close 될 때 같이 close 되네요.
이 글을 보고 혹시나 해서 궁금해서 소스를 뒤져보니 그렇게 되어있네요. 덕분에 배우고 갑니다 ^^
자바로 파일을 복사할 수 있는 방법은 크게 3가지 정도가 있다. InputStream, OutputStream을 이용한 방법, Buffer를 이용한 방법, Channel을 이용한 방법이 그것이다. 물론 Buffer를 이용하면서도 단순히 Stream에 Buffer 필터를 적용할 수도, MappedByteBuffer를 쓸 수도 있고 Channel을 이용하면서도 inputChannel과 outputChannel을 이용하거나 transterTo()를 이용하는 등 다양한 방법을 구사할 수 있다. 여기서는 자바로 구현 할 수 있는 대표적인 파일 복사 코드를 살펴보고 각 코드간의 성능에 대한 이야기도 나눠 보도록 하겠다.
Java입문서등을 통하여 io (Input/Output)부분을 언급하며 나오는 개념이 Stream일 것이다. 스트림의 개념을 설명하고 처음 접하는 코드는 아래와 유사할 것이다. 파일을 인풋스트림으로 읽어들인 후 그 길이만큼 아웃풋 스트림에다 흘려보내는 방식으로 파일을 복사할 수 있다.
FileInputStream inputStream = new FileInputStream(file); FileOutputStream outputStream = new FileOutputStream(saveFullPath);
int bytesRead = 0; byte[] buffer = new byte[1024]; while ((bytesRead = inputStream.read(buffer, 0, 1024)) != -1) { outputStream.write(buffer, 0, bytesRead); }
outputStream.close(); inputStream.close();
* InputStream과 OutputStream을 이용한 기본적인 파일 복사 코드. 위 코드는 기본적인 Stream의 사용법을 잘 보여주고 있지만 성능상에 심각한 문제를 안고 있다. 파일크기(정확하게는 스트림의 길이)만큼 while문을 돌면서 끊임없이 읽고쓰기를 반복하고 있는데 이는 CPU, DISK모두에게 부담을 주는 결과를 초래한다.
이어지는 코드가 아마 가장 널리쓰이고 흔하게 볼수 있는 코드 일 것이다. 위에서 살펴본 Stream간의 데이터 전송이 썩 좋은 성능을 내지 못하기 때문에, 스트림을 버퍼를 장착(wrapping, chainning)하여 입출력 횟수를 줄여 성능 향상을 꾀하고 있다.
FileInputStream inputStream = new FileInputStream(file); FileOutputStream outputStream = new FileOutputStream(saveFullPath);
BufferedInputStream bin = new BufferedInputStream(inputStream); BufferedOutputStream bout = new BufferedOutputStream(outputStream);
int bytesRead = 0; byte[] buffer = new byte[1024];
* Stream에 Buffer Filter를 연결하여 성능을 향상. 위와같은 방법으로 충분히 만족할만 한가? 그렇다고 할수도있고 아니라고 할수도 있다. 위 두 방식은 스트림으로 데이터를 전송하는데 항상 cpu의 연산을 필요로 한다. 즉 스트림을 처리하는동안 cpu가 계속해서 명령을 처리 해줘야 한다는것이다.(비록 cpu사용율은 얼마 안될지 모르지만.. )
컴퓨터의 입장에서 본다면 IO는 상당히 느린 작업중의 하나이다. 이런 작업을 조금이라도 빨리 처리하기위해 하드웨어 혹은 운영체제 수준에서 많은 기법들을 제공하고 있다. 자바는 버전 1.4에 이르러서 기존 io와는 차별화된 nio(new io) 패키지가 추가되었는데 이 nio를 통하여 운영체제가 제공해 주는 향상된 io기능을 활용할 수 있게 되었다. 그 대표적인 것이 Channel과 Selector일 것이다. 아래와 같은 코드는 JDK 1.4이상부터 사용 가능하며 transferTo() 메소드를 호출하면 내부적으로 OS의 네이티브IO 기능을 활용하여 더욱 효율적인 스트림 전송이 가능하다.
FileInputStream inputStream = new FileInputStream(file); FileOutputStream outputStream = new FileOutputStream(saveFullPath);
public class DateDiff { public static int GetDifferenceOfDate ( int nYear1, int nMonth1, int nDate1, int nYear2, int nMonth2, int nDate2 ) { Calendar cal = Calendar.getInstance ( ); int nTotalDate1 = 0, nTotalDate2 = 0, nDiffOfYear = 0, nDiffOfDay = 0;
if ( nYear1 > nYear2 ) { for ( int i = nYear2; i < nYear1; i++ ) { cal.set ( i, 12, 0 ); nDiffOfYear += cal.get ( Calendar.DAY_OF_YEAR ); } nTotalDate1 += nDiffOfYear; } else if ( nYear1 < nYear2 ) { for ( int i = nYear1; i < nYear2; i++ ) { cal.set ( i, 12, 0 ); nDiffOfYear += cal.get ( Calendar.DAY_OF_YEAR ); } nTotalDate2 += nDiffOfYear; }
public String fGetDateTime ( ) { final int millisPerHour = 60 * 60 * 1000; String DATE_FORMAT = "yyyy / MM / dd HH:mm"; SimpleDateFormat sdf = new SimpleDateFormat ( DATE_FORMAT ); SimpleTimeZone timeZone = new SimpleTimeZone ( 9 * millisPerHour, "KST" ); sdf.setTimeZone ( timeZone );
long time = System.currentTimeMillis ( ); Date date = new Date ( time ); return sdf.format ( date ); }
해답:
// 날짜와 요일 구한다. timezone 으로 날짜를 다시 셋팅하시면 됨니다. public String getDate ( ) { Date now = new Date ( ); SimpleDateFormat sdf4 = new SimpleDateFormat ( "yyyy/MM/dd HH:mm EE" ); sdf4.setTimeZone ( TimeZone.getTimeZone ( "Asia/Seoul" ) );
return sdf4.format ( now ); }
날짜와 시간이 유효한지 검사하려면...?
import java.util.*; import java.text.*;
public class DateCheck { boolean dateValidity = true;
public boolean datevalid ( ) { return dateValidity; }
public static void main ( String args [] ) { DateCheck dc = new DateCheck ( "2001-02-28" ); System.out.println ( " 유효한 날짜 : " + dc.datevalid ( ) ); } }
두 날짜 비교하기(아래보다 정확)
그냥 날짜 두개를 long(밀리 세컨드)형으로 비교하시면 됩니다...
이전의 데이타가 date형으로 되어 있다면, 이걸 long형으로 변환하고. 현재 날짜(시간)은 System.currentTimeMillis()메소드로 읽어들이고, 두수(long형)를 연산하여 그 결과 값으로 비교를 하시면 됩니다.
만약 그 결과값이 몇시간 혹은 며칠차이가 있는지를 계산할려면, 결과값을 Calender의 setTimeInMillis(long millis) 메소드를 이용해 설정한다음 각각의 날짜나 시간을 읽어오시면 됩니다
두 날짜 비교하기2
//Calendar를 쓸 경우 데이타의 원본을 고치기 때문에 clone()을 사용하여 //복사한 후에 그 복사본을 가지고 비교한다 import java.util.*; import java.util.Calendar.*; import java.text.SimpleDateFormat;
public class DayComparisonTest { public static void main(String args[]) { Calendar cal = Calendar.getInstance(); SimpleDateFormat dateForm = new SimpleDateFormat("yyyy-MM-dd");
Calendar aDate = Calendar.getInstance(); // 비교하고자 하는 임의의 날짜 aDate.set(2001, 0, 1);
Calendar bDate = Calendar.getInstance(); // 이것이 시스템의 날짜
// 여기에 시,분,초를 0으로 세팅해야 before, after를 제대로 비교함 aDate.set( Calendar.HOUR_OF_DAY, 0 ); aDate.set( Calendar.MINUTE, 0 ); aDate.set( Calendar.SECOND, 0 ); aDate.set( Calendar.MILLISECOND, 0 );
if (aDate.after(bDate)) // aDate가 bDate보다 클 경우 출력 System.out.println("시스템 날짜보다 뒤일 경우 aDate = " + dateForm.format(aDate.getTime())); else if (aDate.before(bDate)) // aDate가 bDate보다 작을 경우 출력 System.out.println("시스템 날짜보다 앞일 경우 aDate = " + dateForm.format(aDate.getTime())); else // aDate = bDate인 경우 System.out.println("같은 날이구만"); } }
using System; using System.Collections.Generic; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; using System.Diagnostics; using Microsoft.Win32;
namespace CoWorkVideoPlayer { /// <summary> /// 본 코드는 System.Window.Control.MediaElement 와 System.Window.Media.MediaFlayer 를 /// 동시에 WPF상에 올려 제어합니다. /// MediaPlayer를 사용할 때 가장 주의해야 할 부분은 이 클래스는 직접적인 시각 표현이 없으며 /// 시각적 트리에 직접 추가할 수 없다는 점입니다. 비디오를 재생하려면 /// MediaPlayer를 가져온 다음 해당 표면을 VisualBrush와 같은 시각적 트리에 추가해야 합니다. /// 기본적으로 MediaElement는 MediaPlayer 클래스의 상위 래퍼입니다 /// </summary>
public partial class Window1 : System.Windows.Window {
//MediaElement 는 xaml영역에 설정되어 있으며 //MediaPlayer는 코드단에서 생성합니다. MediaPlayer mp = new MediaPlayer(); Stopwatch stop = new Stopwatch(); Image videoImage = new Image();
public Window1() { //어플리케이션 윈도우 설정 this.Background = new SolidColorBrush(Color.FromArgb(0, 34, 34, 34)); this.Title = "WPF Video Player"; InitializeComponent(); tbmp.Visibility = Visibility.Hidden; tbme.Visibility = Visibility.Hidden;
//MediaElement의 경우 LoadedBehavior,UnloadedBehavior 를 //Manual로하지않으면 미디어 제어를 할 수 없음 me.LoadedBehavior = MediaState.Manual; me.UnloadedBehavior = MediaState.Manual;
//MediaPlayer표현영역 Rectangle 설정 rec.Stroke = new SolidColorBrush(Colors.Black); rec.Fill = new VisualBrush(videoImage);
//미디어가 오픈되면 TimeSpan을 통해 전체 길이를 알 수 있다. void me_MediaOpened(object sender, RoutedEventArgs e) { if (me.NaturalDuration.HasTimeSpan) { timeSlider.Maximum = me.NaturalDuration.TimeSpan.TotalMilliseconds; System.Diagnostics.Debug.WriteLine(" timeSlider.Maximum : " + timeSlider.Maximum); timeSlider.IsEnabled = true; } }
//이벤트 발생시 WPF의 Image 영역에 이미지를 뿌립니다. private void renda(Object sender, EventArgs e) { try { RenderTargetBitmap rtb = new RenderTargetBitmap(mp.NaturalVideoWidth, mp.NaturalVideoHeight, 1 / 200, 1 / 200, PixelFormats.Pbgra32); DrawingVisual dv = new DrawingVisual(); DrawingContext dc = dv.RenderOpen(); dc.DrawVideo(mp, new Rect(0, 0, mp.NaturalVideoWidth, mp.NaturalVideoHeight)); dc.Close(); rtb.Render(dv); videoImage.Source = BitmapFrame.Create(rtb);