RandomeAccessFile

보통의 자바에서는 입력과 출력이 별도로 분리되어있습니다. 그러니까 InputStream이 있고, OutputStream으로 입력받는 스트림, 그리고 출력하는 스트림이 따로 존재하잖아요? 하지만 RandomAccessFile은 예외적으로 RandomAccessFile이라는 객체 하나로 쓰기와 읽기 둘 다 할 수 있습니다. 사실 RandomAccessFile 클래스는 DataInput, 그리고 DataOutput을 둘다 implements 했기 때문에 읽기, 쓰기가 가능합니다. DataStream 중에 DataInputStream클래스가 DataInput, DataOutputStream클래스가 DataOutput을 구현했습니다. 

RandomAccessFile

 

RandomAccess 파일은 다음 데이터를 읽을지 쓸지 가리키는 Pointer가 존재합니다. 맨 처음 파일이 생성되면 가장 처음 위치인 0의 위치를 가리키고 있다가 쓰거나 읽으면 이 포인터가 다음의 위치로 옮겨가게 됩니다.

1. 데이터 쓰기 - write ~()

아래의 코드는 RandomAccessFile에 파일을 기록하는 프로그램을 구현한 건데요.

public static void main(String[] ar) throws IOException{
	RandomAccessFile raf=new RandomAccessFile("raf_file.dat","rw");
	System.out.println("position:" + raf.getFilePointer());	
		
	raf.writeInt(10);
	System.out.println("position:" + raf.getFilePointer());	//4bytes
		
	raf.writeChar('C');
	System.out.println("position:" + raf.getFilePointer());	//2bytes
		
	raf.writeLong(1010L);
	System.out.println("position:" + raf.getFilePointer());	//8bytes
		
	raf.writeByte(8);
	System.out.println("position:" + raf.getFilePointer());	//1byte
	
	raf.close();
		
}

 

출력을 보면 각각 기록하는 자료형에 따라서 그 포인터의 값이 증가함을 알 수 있습니다. 

position:0
position:4
position:6
position:14
position:15

 

아래의 그림처럼 데이터가 자료형에 따라서 포인터의 다음 자료를 저장할 위치를 가리킵니다. 처음은 아무것도 기록되어 있지 않으니 0의 위치에 있고, 다음 정수형 자료를 기록했으니 다음 입력받을 위치는 4의 위치에 기록하면 됩니다. 

 

문자열을 쓸때는 writeUTF() 메소드를 사용합니다. 여기서는 간단하게 세글자를 써보도록 하겠습니다. 

 
	   //write
	raf.writeUTF("RAF");
	System.out.println(raf.getFilePointer());
		

그렇다면 결과는 3이 되어야하지 않을까요? 하지만 결과는 5가 나오게 됩니다. 그 이유는 파일에 문자열을 쓰는 것은 문제가 없는데 읽을때가 문제가 됩니다. 어디까지 읽어야하는지 알수 없게 되거든요. 그래서 앞 2바이트는 문자열의 길이를 기록합니다. 그래서 총 5바이트가 되는 것이죠.

 

2. Data 읽기 - read~ ()

기록을 해봤으니 이제 데이터를 읽어와볼까요? 읽어오는 메소드도 write와 같은 형태의 메소드명으로 만들어져있습니다. 만약 Int형의 자료를 읽으려면 readInt() 메소드를 사용하면 되겠죠? 아래의 코드가 읽는 코드를 구현한 것입니다.

public static void main(String[] ar) throws IOException{
	RandomAccessFile raf=new RandomAccessFile("raf_file.dat","rw");
    
    //write
	raf.writeInt(10);	
	raf.writeChar('C');	
	raf.writeLong(1010L);	
	raf.writeByte(8);
    
    //read
	System.out.println(raf.readInt());
	System.out.println(raf.readChar());
	System.out.println(raf.readLong());
	System.out.println(raf.readByte());
	
	raf.close();
		
}

 

결과는 EOFException이 발생하여 프로그램이 종료됩니다. EOF(End Of File)는 파일의 마지막이라는 뜻으로 이미 끝인 파일에 read메소드를 수행해서 발생합니다.

Exception in thread "main" java.io.EOFException
	at java.base/java.io.RandomAccessFile.readInt(RandomAccessFile.java:841)
	at reakwon.Main.main(Main.java:17)

 

3. 파일 포인터(위치) 변경 - seek()

이쯤에서 위의 포인터를 그린 그림을 살짝 보면 우리는 이미 포인터가 15의 위치로 이 위치가 EOF였습니다. 그러다가 read로 그 지점부터 읽다가 보니 바로 EOF 예외가 발생하게 되었죠. 따라서 파일 포인터 위치를 맨 처음으로 되돌려야합니다. 그럴때 사용할 수 있는 메소드가 seek 메소드입니다. 여기서 0이 파일의 맨 처음을 의미합니다. 읽기 전에 seek() 메소드로 되돌려보면 정상적으로 읽히고 있음을 확인할 수 있습니다.

		//... 중략 ...
		raf.writeByte(8);
	    
		raf.seek(0);
	    //read
		System.out.println(raf.readInt());
        //... 중략    ...    
}
10
C
1010
8

 

4. 파일의 크기 - length()

파일의 크기를 구하고 싶다면 length() 의 함수를 쓰면 파일의 크기를 구할 수 있습니다. 사실 파일의 크기이기도 하지만  다음 파일이 기록될 위치이기도 하며 EOF입니다. 이때 byte단위로 파일의 크기가 return 됩니다.

이상으로 RandomAccessFile에 대해서 알아보았습니다. 파일을 읽기, 쓰기 스트림 따로 두지 않고 하나의 객체를 통해서 읽기, 쓰기를 한다는 개념입니다. 이때 읽기, 쓰기에 따라 파일의 pointer가 달라진다는 점을 기억하시면 무엇인가 에러가 발생했을때 가장 처음 의심해볼 수 있습니다.

반응형
블로그 이미지

REAKWON

와나진짜

,

SED(Stream Editor)

SED는 Stream Editor의 약자로 sed라는 명령어로 원본 텍스트 파일을 편집하는 유용한 명령어입니다. 리눅스의 editor라하면 생각나는 에디터가 있지 않나요? vi 편집기가 대표적인데, 여러분들이 vi편집기로 편집할때는 vi filename의 명령을 이용해서 파일을 열고 난 이후에 각종 vi 명령어를 통해서 이리 저리 움직여 추가, 삭제, 변경 등의 편집을 하게 되죠. 그리고 작업이 다 끝나게 되면 저장 후 나가게 됩니다. 이때 여러분은 원래의 파일을 변경하여 저장하기 때문에 원본은 변경됩니다. 그리고 vi는 실시간 저장할 수가 있습니다. 여기서 sed는 vi 편집기와는 다르게 이런 차이점이 있습니다.

 

1. sed와 vi가 다른 점은 sed는 명령어 형태로 편집이 되며 vi처럼 실시간 편집이 아닙니다.

2. 원본을 건드리지 않고 편집하기 때문에 작업이 완료되었어도 기본적으로 원본에는 전혀 영향이 없다는 점입니다.(단, 여러분이 sed옵션에서 -i 옵션을 지정한다면 원본을 바꾸게 됩니다.) 그래서 내부적으로 특수한 저장 공간인 버퍼를 사용합니다. 두 가지 버퍼는 패턴 버퍼(패턴 스페이스라고도 합니다)와 홀드 버퍼(홀드 스페이스라고도 합니다)입니다.

 

아래의 그림을 보면 sed는 InputStream으로 파일의 내용을 가져옵니다. 그리고 난 후에 패턴 버퍼에 그 내용을 담고 있으며 데이터의 변형과 추가를 위해 다시 임시 버퍼를 사용하게 되는데, 그게 홀드 버퍼입니다. 그리고 작업이 전부 완료되었다면 패턴 버퍼에 그 내용이 담기는데, 그 내용을 OutputStream으로 보내주게 되면 비로소 우리가 원하는 결과가 출력되는겁니다. 기본적으로 OutputStream은 콜솔화면인 stdout입니다. 우리는 그냥 sed가 내부적으로 2개의 버퍼를 가지고 있다고 보시면 됩니다. 

pattern space and hold space

 

 

이제 sed의 기본적인 개념을 알아보았으니 사용법에 대해서 알아보아야겠죠? 그 전에 sed에 대한 input 파일은 아래의 내용처럼 간단한 텍스트 파일로 하도록 하겠습니다. 필요에 따라 다른 text파일도 사용하도록 하겠습니다.

 

sed_test_file.txt

name    phone           birth           sex     score
reakwon 010-1234-1234   1981-01-01      M       100
sim     010-4321-4321   1999-09-09      F       88
nara    010-1010-2020   1993-12-12      M       20
yut     010-2323-2323   1988-10-10      F       59
kim     010-1234-4321   1977-07-17      M       69
nam     010-4321-7890   1996-06-20      M       75
sol     010-5911-1111   1976-10-12      F       60
lee     010-4949-4949   1988-09-30      F       80
feng    010-1111-9999   1979-03-20      M       90

 

기본적으로 sed는 아래와 같은 형태로 사용합니다. 

sed -n -e 'command' [input file]

 

 

-n : sed는 pattern buffer의 내용을 자동적으로 출력해주는데, 이 옵션을 사용하게 되면 자동 출력을 하지 않습니다. -n옵션을 하지 않고 sed명령을 해보시면 눈에 띄게 많은 데이터가 출력되는것을 확인할 수 있습니다. 매우 지저분하지요. 그래서 -n옵션을 붙여 패턴 버퍼의 자동출력을 하지 않습니다. 여러분은 그래서 sed 명령의 기본 형태는 sed -n으로 생각하시면 됩니다.

 

-e : 이 옵션 다음으로는 우리가 사용할 command를 가지고 텍스트 파일을 가공해줍니다. -e의 command에는 어떤 종류가 있는지 확인해보도록 합시다.

그리고 맨 마지막에는 우리가 가공할 원본 파일을 지정해주면 됩니다. 

1. 특정 행을 출력 - p (print)

1-1. 천재 내용을 출력 : 기본적으로 전체의 줄을 출력하려면 command에 /$/p 또는 1,$p로 출력해볼 수 있습니다. 

sed -n -e '1,$p' sed_test_file.txt
sed -n -e '/$/p' sed_test_file.txt

 

1-2. 첫번째 줄 출력 : 여러분이 첫번째 줄만 출력해주기를 아래의 command를 사용할 수 있습니다. 

 sed -n -e '1p' ./sed_test_file.txt
name    phone           birth           sex     score

 

pprint의 p를 의미합니다. 

1-3. start ~ end 줄까지 출력 : 그렇다면 첫째줄부터 4번째줄을 출력하기를 원한다면 쉼표로 구분된 출력하길 원하는 줄, 마지막 줄 p 의 형태를 사용하면 됩니다.

sed -n -e '1,4p' sed_test_file.txt
name    phone           birth           sex     score
reakwon 010-1234-1234   1981-01-01      M       100
sim     010-4321-4321   1999-09-09      F       88
nara    010-1010-2020   1993-12-12      M       20

 

1-4. 특정줄에서 마지막 줄까지 출력 : 어떤 줄에서 마지막 줄까지 출력하려면 $문자를 사용하면 됩니다. $는 마지막줄을 의미합니다. 그래서 5번째 줄에서 마지막줄까지 출력하려면 아래와 같은 command를 사용할 수 있습니다.

sed -n -e '7,$p' sed_test_file.txt
nam     010-4321-7890   1996-06-20      M       75
sol     010-5911-1111   1976-10-12      F       60
lee     010-4949-4949   1988-09-30      F       80
feng    010-1111-9999   1979-03-20      M       90

 

1-5. 다중 command 사용 : 만약 command를 여러개 사용하고 싶다면 -e 옵션을 이용해서 여러개 사용하여 command를 줄 수 있습니다. 만약 1번째 줄을 출력해주고, 8번째 줄부터 끝까지 출력하기를 원한다면 아래의 명령을 사용하면 됩니다.

sed -n -e '1p' -e '8,$p'  sed_test_file.txt
name    phone           birth           sex     score
sol     010-5911-1111   1976-10-12      F       60
lee     010-4949-4949   1988-09-30      F       80
feng    010-1111-9999   1979-03-20      M       90

 

1-6. 특정 문자열이 있는 줄 출력

만약 위의 파일에서 여자(F, Female)만 출력한다고 하다면 어떻게 command를 어떻게 사용할까요? 아래와 같은 형식대로 사용하면 됩니다.

/포함된 문자열/p

 

텍스트 파일에서 우리가 찾을 문자열이 포함된 줄을 출력(p, print)한다고 생각하시면 됩니다. 아래의 명령이 여성만 뽑아내는 command입니다.

sed -n -e '/F/p'  sed_test_file.txt

 

2. 특정 행 삭제 - d (delete)

행삭제에 관한 명령어d를 사용하면 됩니다. d를 사용하는 것외에는 위의 줄 출력을 해주는 명령어 형태로 사용하면 됩니다. 

2-1. 2~6번째 줄을 삭제하고 나머지 모든 내용을 출력

sed -n -e '2,6d' -e '1,$p'  sed_test_file.txt
name    phone           birth           sex     score
nam     010-4321-7890   1996-06-20      M       75
sol     010-5911-1111   1976-10-12      F       60
lee     010-4949-4949   1988-09-30      F       80
feng    010-1111-9999   1979-03-20      M       90

 

3. 단어 치환(Substitute) - s (substitute)

이제부터는 출력결과는 생략하도록 하겠습니다. 특정 단어를 치환하려면 s명령을 이용하면 됩니다. 형식은 아래와 같습니다. 

3.1 기본적인 특정 단어 치환

s/old/new/g
s/old/new/gi

 

단어 s substitute(치환)의 약자, 그리고 /로 구분하여 old는 단어를 치환할 문자열, new는 새롭게 치환한 문자열인데 비어있으면 그 문자열을 삭제한 효과를 가질 수 있습니다. gglobal의 약자로 전체에 적용됨을 의미합니다. 

두번째의 i는 ignore case의 약자로 old의 단어를 검색할때 대소문자 구분하지 않겠다는 것을 의미합니다. 

아래 명령의 결과를 보고 차이점을 확인해보세요.

sed -n -e 's/reakwon/reak/g' -e '2p' sed_test_file.txt
sed -n -e 's/reakwoN/reak/g' -e '2p' sed_test_file.txt
sed -n -e 's/reakwoN/reak/gi' -e '2p' sed_test_file.txt

 

3.2 특정 단어로 시작 혹은 끝나는 단어를 포함하는 라인의 문자열 치환

특정단어로 시작하는 단어로 문자열을 치환하는 것도 가능합니다. 특정 단어로 시작하는 줄을 선택하려면 앞에 '^'문자를 사용하면 되죠. 사실 정규표현식을 아신다면 앞에 ^가 붙는다면 ^이후에 나오는 문자열로 시작되는 문자열들을 추출한다는 것을 아실겁니다. 사실 몰라도 외워서 아시면 됩니다. 단 많이 실수하시는 부분이 특정 단어로 시작하거나 끝나는 문자열이 아닌 줄(line)입니다.

 

아래의 파일(let_it_go.txt)에서 Let으로 시작하는 줄의 첫 Let를 LET으로 바꿔보려면 다음 보이는 명령을 사용하면 됩니다.

Let it go, let it go.
Can't hold it back anymore.
Let it go, let it go.
Turn away and slam the door.
I don't care what they're going to say.
Let the storm rage on.
The cold never bothered me anyway.

 

sed -n -e 's/^let/LET/gi' -e '1,$p' let_it_go.txt

 

반대로 끝나는 문자열은 끝에 $를 붙여줘서 검색하면 됩니다. Anyway. 으로 끝나는 줄의 Anyway를 대문자로 바꾸려면 아래와 같은 command를 사용하면 됩니다.

sed -n -e 's/anyway.$/ANYWAY/gi' -e '1,$p' let_it_go.txt

 

4. 문자열 추가 - a, i (append, insert)

문자열을 추가하는 방법에는 두 가지 정도가 존재합니다. 해당 문자열 아래에 추가하느냐(Append) 아니면 이 전 줄에 삽입하느냐(Insert)가 있는데요. 기본적인 형식은 아래의 command처럼 사용합니다.

/찾을 문자열/a\다음 출에 추가할 문자열
/찾을 문자열/i\위에 삽입할 문자열

 

찾을 문자열 뒤에 추가할 문자열을 위의 형식대로 사용하면 됩니다. 위의 let_it_go.txt파일에서 bye로 끝나는 줄 다음에 end라는 줄을 넣고 싶다면 이렇게 사용하면 됩니다. 반대로 위에 추가하려면 a를 i로 바꾸면 되겠죠.

 

 

sed -n -e '/bye/a\end' -e '1,$p' let_it_go.txt

 

5. 특정 행의 내용을 전부 교체 - c (change)

c command를 이용해서 행의 내용을 바꿀 수 있습니다. command 형태는 이렇습니다.

/바꿀 행이 포함한 문자열/c\바꿀 행의 내용

 

예를 들어 Let으로 시작하는 줄의 내용을 바꾸고 싶다면 어떻게 할까요? 우선 ^를 사용하여 Let으로 시작하는 줄들을 찾고 c 커맨드로 바꿔질 줄 내용을 입력해주시면 됩니다.

sed -n -e '/^Let/c\Let it go X2' -e '1,$p' let_it_go.txt

 

6. 특정 행에 파일의 내용을 추가 - r (read)

혹은 파일의 내용을 줄에다가 추가할 수도 있습니다. 여기 우리가 내용을 추가할 파일이 존재합니다.

 

perfect.txt

PERFECT! EXCELLENT!

 

100으로 끝나는 줄에 저 텍스트 파일의 내용을 아랫줄에 첨가하려면 아래와 같은 명령을 사용하면 됩니다.

 sed -n -e '/100$/r perfect.txt' -e '1,$p' sed_test_file.txt
name    phone           birth           sex     score
reakwon 010-1234-1234   1981-01-01      M       100
PERFECT! EXCELLENT!
sim     010-4321-4321   1999-09-09      F       88
nara    010-1010-2020   1993-12-12      M       20
yut     010-2323-2323   1988-10-10      F       59
kim     010-1234-4321   1977-07-17      M       69
nam     010-4321-7890   1996-06-20      M       75
sol     010-5911-1111   1976-10-12      F       60
lee     010-4949-4949   1988-09-30      F       80
feng    010-1111-9999   1979-03-20      M       90

 

이상으로 sed에 대한 기본 개념과 사용방법, 그리고 예제를 살펴보았습니다. 여러분이 정규표현식을 사용하여 깊게 사용할 수도 있지만, 저는 정규표현식을 거의 사용할 일이 없으니 여기까지 하도록 하겠습니다. 이정도만 알아도 sed 명령을 사용하는 것에는 불편함이 없을 겁니다.

 

 

반응형
블로그 이미지

REAKWON

와나진짜

,