일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- word2vec
- 딥러닝
- 수동설치
- Crawling
- Lamp
- 한빛미디어
- 머신러닝
- 컴파일설치
- AndroidStudio를활용한안드로이드프로그래밍
- Apache
- 셀레니움
- 생활코딩
- jupyter
- 밑바닥부터시작하는딥러닝
- deeplearning
- 크롤링
- 가비아
- attention
- Selenium
- 프로그램새내기를위한자바언어프로그래밍
- image
- 소스설치
- MySQL
- 비지도학습
- 밑바닥부터시작하는딥러닝2
- 논문리뷰
- 한빛아카데미
- aws
- CBOW
- 예제중심HTML&자바스크립트&CSS
- Today
- Total
안녕, 세상!
8. 파일 처리 본문
(1) 파일 처리의 기본
안드로이드에서 파일을 읽고 쓰는 방법에는 Java에서 제공되는 파일 관련 클래스를 사용하는 방법과 안드로이드에서 제공되는 파일 관련 클래스를 사용하는 방법이 있습니다.
① 내장 메모리 파일 처리
앱을 종료했다가 다음에 다시 실행할 때 사용했던 곳부터 이어서 작업하고 싶은 경우 내장 메모리에 파일을 저장하고 읽어오는 방식이 사용됩니다.
내장 메모리의 저장 위치는 /data/data/패키지명/files 폴더입니다.
일반적으로 응용 프로그램마다 다른 패키지명을 사용하므로 응용 프로그램별로 고유의 저장 공간이 있다고 생각하면 됩니다.
파일을 읽기 위해 먼저 안드로이드 Context클래스의 openFileInput() 메소드를 사용합니다.
이 메소드는 FileInputStream을 반환합니다.
파일을 쓰기 위해 openFileOutput() 메소드를 사용하면 FileOutputStream을 반환합니다.
그리고 java에서 제공하는 파일을 읽고 쓰는 java.io.FileInputStream 클래스와 java.io.FileOutputStream의 read(), write() 메소드를 사용하여 파일을 처리합니다.
AVD 에뮬레이터의 /data/data/패키지명/files 폴더에 접근 방법은 다음과 같습니다.
1. 에뮬레이터를 우선 [run] 시킵니다.
2. [Shift]를 두 번 연속 클릭 후 "Device file explorer"라고 검색합니다.
3. 왼쪽 창에 AVD의 파일 시스템을 확인할 수 있습니다.
4. /data/data/패키지명/files에 접근할 수 있습니다.
다음은 내장 메모리에 데이터를 저장해서 특정 날짜에 작성한 글이 저장되게 하는 예제입니다.
activity_main.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<DatePicker
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:datePickerMode="spinner"
android:calendarViewShown="false"
android:id="@+id/datePicker1" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/edtDiary"
android:background="#6600ff00"
android:lines="8" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/btnWrite"
android:enabled="false"
android:text="Button" />
</LinearLayout>
|
cs |
DatePicker의 id는 datePicker1
EditText의 id는 edtDiary
Button의 id는 btnWrite
MainActivity.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
|
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Calendar;
public class MainActivity extends AppCompatActivity {
DatePicker dp;
EditText edtdiary;
Button btnwrite;
String filename;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setTitle("일기장");
dp= (DatePicker) findViewById(R.id.datePicker1);
edtdiary = (EditText) findViewById(R.id.edtDiary);
btnwrite = (Button) findViewById(R.id.btnWrite);
Calendar cal = Calendar.getInstance();
int cYear = cal.get(Calendar.YEAR);
int cMonth = cal.get(Calendar.MONTH);
int cDay = cal.get(Calendar.DAY_OF_MONTH);
dp.init(cYear, cMonth, cDay, new DatePicker.OnDateChangedListener() {
@Override
public void onDateChanged(DatePicker view, int year, int monthofyear, int dayofmonth) {
filename=Integer.toString(year)+"-"+Integer.toString(monthofyear+1)+"-"
+Integer.toString(dayofmonth)+".txt";
String str = readDiary(filename);
edtdiary.setText(str);
btnwrite.setEnabled(true);
}
});
btnwrite.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
try{
FileOutputStream outfs = openFileOutput(filename, Context.MODE_PRIVATE);
String str = edtdiary.getText().toString();
outfs.write(str.getBytes());
outfs.close();
Toast.makeText(getApplicationContext(), filename+"이 저장됨", Toast.LENGTH_SHORT).show();
}catch (IOException e){
}
}
});
}
String readDiary(String fname){
String diarystr = null;
FileInputStream infs;
try{
infs = openFileInput(fname);
byte[] txt = new byte[500];
infs.read(txt);
infs.close();
diarystr = (new String(txt).trim());
btnwrite.setText("수정하기");
}catch (IOException e){
edtdiary.setHint("일기 없음");
btnwrite.setText("새로 저장");
}
return diarystr;
}
}
|
cs |
java코드는 크게 3 부분으로 나뉩니다.
36~45, 47~60, 62~78로 나뉩니다.
36~45 : DatePicker이 변동될 시 동작하는 메소드입니다.
38~40 : DatePicker이 변동되면 변동된 연월일의 정보를 filename이라는 변수에 .txt라는 문자열로 저장을 합니다.
41 : readDiary라는 메소드를 실행
42 : 반환받은 데이터를 editdiary에 넣습니다.
43 : activity_main.xml에서 비활성화시킨 버튼을 다시 활성화시킵니다.
62~78 : readDiary메소드
66 : fname에 해당되는 파일을 열어서 입력 파일 스트림에 내용을 저장합니다. 파일이 없으면 catch문이 실행됩니다.
67~68 : byte형 변수 txt를 선언하고 파일 내용을 txt에 읽어 들입니다.
69 : 파일을 닫습니다.
70 : 읽어온 txt를 문자열로 변경한 후 trim() 메소드로 앞뒤의 공백을 제거하고 반환할 diarystr 변수에 대입합니다.
71 : 내용이 한 번 저장되었으니 버튼을 "수정하기"로 바꿉니다.
42 : 반환받은 데이터를 editdiary에 넣습니다.
43 : activity_main.xml에서 비활성화시킨 버튼을 다시 활성화시킵니다.
50 : filename변수에 있는 파일을 쓰기 모드로 아웃풋 스트림에 저장합니다. .txt파일도 생성됩니다.
51 : editdiary에 있는 내용을 String형태로 가지고 와서 str변수에 저장 후 str변수의 내용을 byte[] 형으로 아웃풋 스트림에 write 합니다.(filename변수에 있는 .txt파일에 write 한다는 의미)
처음에 DatePicker을 한 번 굴려주면 위와 같이 나옵니다.
일기에 내용을 입력하고 '새로저장' 버튼을 클릭하면 다음과 같이 Toast가 출력됩니다.
DatePicker을 다른 날짜로 돌렸다가 다시 돌아오면 다음과 같이 버튼이 '수정하기'로 바뀌고 입력한 데이터가 보존된 것을 확인할 수 있습니다.
생성된 .txt파일은 Device File Explorer에서도 확인할 수 있습니다.
② raw 폴더 파일 처리
Project Tree의 [app] - [res] 폴더에 raw 파일을 생성해서 필요한 파일을 저장해서 사용하는 방법도 있습니다.
Java 소스에서 openRawResource() 메소드를 사용하여 접근할 수 있으며 FileInputStream 클래스 대신 InputStream 클래스를 사용합니다.
/res/raw는 프로젝트에 포함된 폴더이므로 읽기 전용으로만 사용 가능하고 쓰기는 할 수 없습니다.
주요 동작 코드는 다음과 같습니다.
try {
InputStream inputS = getResources().openRawResource(R.raw.raw_test);
byte[] txt = new byte[inputS.available()];
inputS.read(txt);
edtRaw.setText(new String(txt)); // edtRaw는 EditText 변수
inputS.close();
}catch (IOException e) { }
(2) 파일 처리의 응용
특정 파일들이 여러 응용 프로그램에서 사용되는 경우에는 SD 카드에 저장하여 활용해야 됩니다.
안드로이드에서는 SD카드에 저장된 데이터에 특별한 인증 절차 없이 접근할 수 있습니다.
제한된 공간의 내장 메모리보다 훨씬 큰 공간을 사용할 수 있으며 확장성도 뛰어납니다.
AVD에도 SD카드를 장착할 수 있는데 방법은 다음과 같습니다.
메뉴의 [Tools] -> [AVD Manager] 의 에뮬레이터의 오른쪽에 Edit 아이콘을 클릭 후 [Show Advanced Settings]를 클릭하면 사용하는 AVD에 장착된 SD카드를 확인할 수 있습니다.
원하는 크기를 설정하고 [Finish] 버튼을 누르면 됩니다.
① SD 카드에서 파일 읽기, 폴더 및 파일 생성하기 예제
아무 .txt파일(내용이 담긴)을 호스트 컴퓨터에 만들어 놓고 Device File Explorer에서 /sdcard 폴더 또는 /storage/emulator/0 폴더에 호스트 컴퓨터에 만든 텍스트 파일을 올려놓습니다.
오른쪽 마우스 클릭 후 [upload]를 눌러서 파일을 올려놓으면 됩니다.
AndroidManifest.xml
AndroidManifest.xml 파일에 SD 카드를 사용할 수 있도록 퍼미션을 지정해야 합니다.
다음 코드를 AndroidManifest.xml에 넣습니다. (<application 부분은 아래 코드 부분만 넣으면 됨)
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:requestLegacyExternalStorage="true"
이 AndroidManifest.xml 설정은 ① ② ③ 예제 모두 적용해야 합니다.
다음은 SD카드에 저장된 파일의 내용을 불러오는 기능과 SD카드에 파일 또는 디렉터리(폴더)를 생성할 수 있는 기능을 가진 코드의 예제입니다.
저는 sdcard 디렉터리에 sdtest.txt라는 텍스트 파일에 '가나다라마바사'라고 저장해 놓았습니다.
acvitity_main.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btnread"
android:text="SD카드에서 파일 읽기" />
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/edtSD"
android:lines="10" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btnmkdir"
android:text="SD카드에 디렉터리 생성" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btnrmdir"
android:text="SD카드에 디렉터리 삭제" />
</LinearLayout>
|
cs |
AcitivyMain.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
import android.Manifest;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnRead;
final EditText edtSD;
btnRead = (Button) findViewById(R.id.btnread);
edtSD = (EditText) findViewById(R.id.edtSD);
ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, MODE_PRIVATE);
btnRead.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
FileInputStream infs = new FileInputStream("/sdcard/sdtext.txt");
byte[] txt = new byte[infs.available()];
infs.read(txt);
edtSD.setText(new String(txt));
infs.close();
} catch (IOException e) {
}
}
});
Button btnmkdir, btnrmdir;
btnmkdir = (Button) findViewById(R.id.btnmkdir);
btnrmdir = (Button) findViewById(R.id.btnrmdir);
final String strSDpath = Environment.getExternalStorageDirectory().getAbsolutePath();
final File myDir = new File(strSDpath+"/mydir");
btnmkdir.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
myDir.mkdir();
}
});
btnrmdir.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
myDir.delete();
}
});
}
}
|
cs |
19~37이 sdtest.txt의 데이터를 불러오는 기능의 코드입니다.
39~55가 sdcard디렉터리에 mydir라는 파일을 생성하고 제거할 수 있는 기능의 코드입니다.
23 : 이 앱에게 파일 액세스 작업을 허용할지 묻는 창이 나옵니다.
27~35 : InputStream에 /sdcard/sdtest.txt 파일의 데이터를 불러서 EditText인 edtSD에 넣고 출력해줍니다.
42 : SD카드의 절대 경로를 돌려주는데, 대개 /storage/emulated/0 또는 /sdcard 경로가 됩니다.
저는 /sdcard로 설정되어 있습니다.
② 특정 폴더의 하위 폴더 및 파일 목록 출력 예제
activity_main.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btnfilelist"
android:text="시스템 폴더의 폴더/파일 목록" />
<EditText
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/edtfilelist" />
</LinearLayout>
|
cs |
MainActivity.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import androidx.appcompat.app.AppCompatActivity;
import java.io.File;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnFilelist;
final EditText edtFilelist;
btnFilelist = (Button) findViewById(R.id.btnfilelist);
edtFilelist = (EditText) findViewById(R.id.edtfilelist);
btnFilelist.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
String sysDir = Environment.getRootDirectory().getAbsolutePath();
File[] sysFiles = (new File(sysDir).listFiles());
String strfname;
for(int i=0; i<sysFiles.length; i++){
if (sysFiles[i].isDirectory()==true){
strfname="<폴더> "+sysFiles[i].toString();
}
else{
strfname="<파일> "+sysFiles[i].toString();
}
edtFilelist.setText(edtFilelist.getText()+"\n"+strfname);
}
}
});
}
}
|
cs |
23 : 안드로이드의 시스템 폴더 경로를 돌려줍니다. 대개 /system이 됩니다.
24 : 시스템 폴더의 폴더/파일 개수만큼 반복합니다.
27~33 : 현재 파일이 폴더인지 확인해서 폴더이면 앞에 <폴더> 글자를 파일이면 <파일> 글자를 붙입니다.
34 : 파일/폴더 목록을 에디트 텍스트에 이어서 출력합니다.
③ 이미지 뷰어 앱
저장된 이미지를 넘기면서 볼 수 있는 앱의 예제입니다.
우선 새로운 클래스가 필요하므로 Project Tree의 [java] -> [패키지명]에서 오른쪽 버튼을 클릭한 후 [New] -> [Java Class]를 선택해서 'myPictureview'라는 이름으로 클래스를 생성하겠습니다.
그다음 미리 사용할 사진들을 [Device File Explorer]에서 /sdcard/pictures 폴더에 적당한 이미지 파일을 upload 해둡니다.
myPictureview.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.Nullable;
public class myPictureview extends View {
String imagePath=null;
public myPictureview(Context context, @Nullable AttributeSet attrs){
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(imagePath !=null) {
Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
canvas.drawBitmap(bitmap, 0, 0, null);
bitmap.recycle();
}
}
}
|
cs |
19~22 : imagePath에 값이 있으면 화면에 그림 파일을 출력한다는 의미입니다. (Bitmap 클래스는 다음 글에서 설명)
activity_main.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btnPrev"
android:layout_weight="1"
android:text="이전 그림"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btnNext"
android:layout_weight="1"
android:text="다음 그림" />
</LinearLayout>
<com.example.myapplication7.myPictureview
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/myPictureview1" />
</LinearLayout>
|
cs |
AndroidManifest.xml
①에서 말한 대로 적용
MainActivity.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.Manifest;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import java.io.File;
public class MainActivity extends AppCompatActivity {
Button btnPrev, btnNext;
myPictureview myPicture; // myPictureview는 제가 따로 만든 클래스, 즉 제가 만든 이미지용 위젯
int num;
File[] imageFiles;
String imageFname;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setTitle("이미지 뷰어");
ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE},MODE_PRIVATE);
btnPrev = (Button) findViewById(R.id.btnPrev);
btnNext = (Button) findViewById(R.id.btnNext);
myPicture = (myPictureview) findViewById(R.id.myPictureview1);
imageFiles=new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/Pictures").listFiles();
imageFname=imageFiles[0].toString();
myPicture.imagePath = imageFname;
btnPrev.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
if(num <=0) {
Toast.makeText(getApplicationContext(), "첫번째 그림입니다.", Toast.LENGTH_SHORT).show();
}
else{
num --;
imageFname = imageFiles[num].toString();
myPicture.imagePath=imageFname;
myPicture.invalidate();
}
}
});
btnNext.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
if(num>=imageFiles.length-1){
Toast.makeText(getApplicationContext(), "마지막 그림입니다.",Toast.LENGTH_SHORT).show();
}
else{
num++;
imageFname = imageFiles[num].toString();
myPicture.imagePath=imageFname;
myPicture.invalidate();
}
}
});
}
}
|
cs |
14 : myPicture변수는 제가 앞서 만든 클래스를 이용한 위젯입니다.
29 : SD카드에 파일을 읽어서(/sdcard/pictures 안의 파일들) listFiles() 메소드로 배열을 만듭니다.
30 : String형 변수 imageFname에 첫 번째 사진 파일명을 저장합니다.
31 : 저장한 파일명을 myPicture 위젯 안에 있는 imagePath에 저장합니다.
33~45 : 이전 버튼을 클릭할 때 처음 사진이라면 Toast를 나타나게 하고 아니면 이전 사진 나오게 합니다.
40~41 : 현재 번호의 이미지 파일 이름을 myPicture 클래스에 전달합니다.
42 : invalidate() 메소드를 호출하면 myPicture 클래스의 onDraw()가 호출됩니다. (다음 글에서 설명)
47~59 : 다음 버튼을 클릭할 때 마지막 사진이면 Toast를 나타나게 하고 아니면 다음 사진 나오게 합니다.
'It공부 > 안드로이드' 카테고리의 다른 글
10. 액티비티와 인텐트 (0) | 2020.08.26 |
---|---|
9. 그래픽 (0) | 2020.08.25 |
7.메뉴와 대화상자 (0) | 2020.08.23 |
6. 고급 위젯 (0) | 2020.08.22 |
5. 레이아웃 (0) | 2020.08.21 |