본문 바로가기

프로그래밍/안드로이드

안드로이드 csv 파일 선택해서 불러오기

728x90
반응형

startactivityforresult deprecated 이전 버전에서는 지원이 중단되기 때문에 새로운 방법으로 파일을 선택하고 불러와야한다. 방법은 ActivityResultLauncher를 사용하는것

 

https://developer.android.com/training/basics/intents/result?hl=ko 

 

활동에서 결과 가져오기  |  Android 개발자  |  Android Developers

활동에서 결과 가져오기 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 개발자 앱 내의 활동이든 다른 앱의 활동이든 다른 활동을 시작하는 것이 단방향 작

developer.android.com

ActivityResultLauncher<String> mGetContent = registerForActivityResult(new GetContent(),
    new ActivityResultCallback<Uri>() {
        @Override
        public void onActivityResult(Uri uri) {
            // Handle the returned Uri
        }
});

@Override
public void onCreate(@Nullable savedInstanceState: Bundle) {
    // ...

    Button selectButton = findViewById(R.id.select_button);

    selectButton.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View view) {
            // Pass in the mime type you'd like to allow the user to select
            // as the input
            mGetContent.launch("text/*");
        }
    });
}

 

파일을 선택해서 불러오는 코드는 안드로이드 개발자 사이트에 잘 정의되어있다.

 


 

안드로이드 앱에서 CSV 파일을 선택하여 데이터를 파싱하기

앱에서 csv 파일을 다루는 것을 공부하기 위해 먼저 다음 순서대로 앱을 만들어보려고 한다.

 

1. csv 파일 파싱하여 데이터 추출
2. csv 파일 선택하여 불러오기
3. 불러온 csv 파일 파싱하여 데이터 추출

 

1. csv 파일 파싱하여 데이터 추출하기

csv 파일은 , 콤마로 구분 되어있는 파일이다. 크게 어려운건 없으니 잘따라한다면 쉽게 할 수 있을것이다.

 

 

item.csv
0.00MB

 

이러한 형식의 임의의 csv 파일이 있다. 방금 대충 만들어 봤는데 연습하기에는 문제는 없을 것 같다.

지금은 csv 파일을 파싱하여 리스트에 넣기만 할거라서 res 풀더에 raw 풀더를 만들어주고 해당 csv를 넣어보자.

 

 

해당 csv 파일을 보자 1행에 1001, 2023-05-15, 창원, 맑음, 좋음 이 적혀있고

2행에 1002, 2023-05-16, 창원, 비, 좋음이 적혀있다

 

각각의 행마다 데이터를 뽑아내서 하나의 객체로 만들려고한다. 그래야 데이터를 다루는데 편리하기 때문이다.

 

package com.example.myapplication;

public class Item {
    private int id;    // 고유 아이디
    private String date;    // 날짜
    private String name;    // 지역
    private String weather;    // 날씨
    private String fineDust;    // 미세먼지

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getWeather() {
        return weather;
    }

    public void setWeather(String weather) {
        this.weather = weather;
    }

    public String getFineDust() {
        return fineDust;
    }

    public void setFineDust(String fineDust) {
        this.fineDust = fineDust;
    }
}

 

클래스는 원하는 곳에 만들면 되고 각열의 의미에 따라서 클래스 변수를 정의해줬다.

그러면 클래스는 만들었고 csv 파일을 파싱하는 csv 파싱 헬퍼를 만들어보자

 

import android.content.Context;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

public class CSVParser {

    public CSVParser() {
        // 빈 생성자
    }

    public List<Item> getItemList(Context context) throws IOException {
        InputStream is = context.getResources().openRawResource(R.raw.item);    // res/raw/item.csv 파일을 불러오기 위해 해당 코드 작성
        BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));

        String line = "";

        List<Item> itemList = new ArrayList<>();

        while ((line = reader.readLine()) != null) {
            String[] tokens = line.split(",");    // 각 행을 순차적으로 돌면서 , 를 기준으로 분리하여 배열에 저장한다.

            Item item = new Item();
            item.setId(Integer.parseInt(tokens[0]));    // 고유 아이디를 클래스 id변수에 저장. 배열에 분리된 데이터는 string 형태기 때문에 int형으로 형변환 해줘야함
            item.setDate(tokens[1]);    // 날짜 저장
            item.setName(tokens[2]);    // 지역 이름 저장
            item.setWeather(tokens[3]); // 날씨 저장
            item.setFineDust(tokens[4]);    // 미세먼지 상태 저장

            itemList.add(item); // 반환할 리스트에 파싱된 행 데이터 저장
        }

        reader.close();
        is.close();

        return itemList;
    }
}

 

input stream과 buffered reader를 이용해서 csv 파일을 불러오고 버퍼에 저장한다.

 

line 변수에 저장된 버퍼에서 한줄씩 읽어온 것을 저장하는데 마지막 줄에서 또 읽게 되면 아무것도 없기 때문에 null이 된다. 그래서 while 조건문을 (line=reader.readLine()) != null 이렇게 하게 되면 줄이 있을 때만 반복문이 돌기 때문에 안전하게 버퍼에서 각 행을 읽어올 수 있다.

 

읽어온  행의 데이터들은 String[] 배열에 split 메서드를 이용한다. , 콤마를 기준으로 문자열을 분리하여 배열에 저장하면된다. 저장된 배열의 인덱스로 접근하여 만든 클래스 객체에 저장하고 리스트에 저장하면 csv 파일을 파싱하는 것은 끝이다.

 

2. 내 파일에서 csv 파일 불러오기 

안드로이드 앱에서 스마트폰에 저장되어있는 csv 파일을 선택해서 불러오고 이전에 만든 csv parser 클래스로

데이터를 파싱하고 리스트로 받아보는 것이 최종적인 작업인데, 그럴려면 csv 파일을 선택해야한다.

csv 파일을 선택해서 불러오는 것은 정말 쉬운데 

 

https://developer.android.com/training/basics/intents/result?hl=ko#java 

 

활동에서 결과 가져오기  |  Android 개발자  |  Android Developers

활동에서 결과 가져오기 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 앱 내에서든 다른 앱에서든 다른 활동을 시작하는 것은 단방향 작업이 아니어도 됩

developer.android.com

안드로이드 개발자 사이트에 ActivityResultLauncher를 이용해서 결과를 받아오는 예제가 있다. 우리는 이것을 따라해보려고 한다.

 

package com.example.myapplication;

import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;

import android.net.Uri;
import android.os.Bundle;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private List<Item> items = new ArrayList<Item>();   // csv 데이터를 이 리스트에 넣을거임

    private final ActivityResultLauncher<String> mGetContent = registerForActivityResult(
            new ActivityResultContracts.GetContent(),
            new ActivityResultCallback<Uri>() {
                @Override
                public void onActivityResult(Uri uri) {
                    // Handle the returned Uri
                }
            });

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mGetContent.launch("text/*");

    }


}

 

버튼을 만들던지 그건 자유고 나는 귀찮으니 앱을 키면 바로 선택 창이 나오도록 해봤다. 실행해보자.

 

바로 이렇게 선택할 수 있도록 파일 데이터가 나온다. 선택이 되지 않는 사람은 아마 코드를 그대로 복붙했기 때문에 확장자 문제일것이다. 

 

 

launch("text/*");을 주의해서 적어보자

 

그러면 선택해서 그 파일의 경로가 uri 매개변수를 통해서 전달받을 수 있을거다. 이걸 input stream에 넣으려면 이렇게 하면 된다.

 

private final ActivityResultLauncher<String> mGetContent = registerForActivityResult(
            new ActivityResultContracts.GetContent(),
            new ActivityResultCallback<Uri>() {
                @Override
                public void onActivityResult(Uri uri) {
                    // Handle the returned Uri
                    try {
                        InputStream is = getContentResolver().openInputStream(uri);
                        BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
                        Log.d("csv", reader.readLine());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });

 

임시로 한줄만 읽었을 때 결과는 이렇게 나온다.

 

 

원하는 결과가 제대로 나온다

예전에는 절대 경로로 변환하고 삽질을 했지만 그러지 않아도 되는걸 깨닫고 후회중이다 ㅠㅠ

 

암튼 이제 이걸 파서 클래스로 제대로 파싱해보자 

 

3. 불러온 csv 파일로 파싱하기

아까 만들었던 csv parser 클래스에 메서드를 추가해주자 같은 이름이지만 매개변수가 다른 메서드이다.

public List<Item> getItemList(Context context, Uri uri) throws IOException {
        InputStream is = context.getContentResolver().openInputStream(uri);
        BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));

        String line = "";

        List<Item> itemList = new ArrayList<>();

        while ((line = reader.readLine()) != null) {
            String[] tokens = line.split(",");    // 각 행을 순차적으로 돌면서 , 를 기준으로 분리하여 배열에 저장한다.

            Item item = new Item();
            item.setId(Integer.parseInt(tokens[0]));    // 고유 아이디를 클래스 id변수에 저장. 배열에 분리된 데이터는 string 형태기 때문에 int형으로 형변환 해줘야함
            item.setDate(tokens[1]);    // 날짜 저장
            item.setName(tokens[2]);    // 지역 이름 저장
            item.setWeather(tokens[3]); // 날씨 저장
            item.setFineDust(tokens[4]);    // 미세먼지 상태 저장

            itemList.add(item); // 반환할 리스트에 파싱된 행 데이터 저장
        }

        reader.close();
        is.close();

        return itemList;
    }

 

CSVParser.java 클래스 전체 코드

package com.example.myapplication;

import android.content.Context;
import android.net.Uri;
import android.util.Log;

import androidx.activity.result.ActivityResultCallback;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

public class CSVParser {

    public CSVParser() {
        // 빈 생성자
    }

    public List<Item> getItemList(Context context) throws IOException {
        InputStream is = context.getResources().openRawResource(R.raw.item);    // res/raw/item.csv 파일을 불러오기 위해 해당 코드 작성
        BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));

        String line = "";

        List<Item> itemList = new ArrayList<>();

        while ((line = reader.readLine()) != null) {
            String[] tokens = line.split(",");    // 각 행을 순차적으로 돌면서 , 를 기준으로 분리하여 배열에 저장한다.

            Item item = new Item();
            item.setId(Integer.parseInt(tokens[0]));    // 고유 아이디를 클래스 id변수에 저장. 배열에 분리된 데이터는 string 형태기 때문에 int형으로 형변환 해줘야함
            item.setDate(tokens[1]);    // 날짜 저장
            item.setName(tokens[2]);    // 지역 이름 저장
            item.setWeather(tokens[3]); // 날씨 저장
            item.setFineDust(tokens[4]);    // 미세먼지 상태 저장

            itemList.add(item); // 반환할 리스트에 파싱된 행 데이터 저장
        }

        reader.close();
        is.close();

        return itemList;
    }

    public List<Item> getItemList(Context context, Uri uri) throws IOException {
        InputStream is = context.getContentResolver().openInputStream(uri);
        BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));

        String line = "";

        List<Item> itemList = new ArrayList<>();

        while ((line = reader.readLine()) != null) {
            String[] tokens = line.split(",");    // 각 행을 순차적으로 돌면서 , 를 기준으로 분리하여 배열에 저장한다.

            Item item = new Item();
            item.setId(Integer.parseInt(tokens[0]));    // 고유 아이디를 클래스 id변수에 저장. 배열에 분리된 데이터는 string 형태기 때문에 int형으로 형변환 해줘야함
            item.setDate(tokens[1]);    // 날짜 저장
            item.setName(tokens[2]);    // 지역 이름 저장
            item.setWeather(tokens[3]); // 날씨 저장
            item.setFineDust(tokens[4]);    // 미세먼지 상태 저장

            itemList.add(item); // 반환할 리스트에 파싱된 행 데이터 저장
        }

        reader.close();
        is.close();

        return itemList;
    }
}

 

 

바뀐점은 uri를 매개변수로 받아서 inputstream에 넣고 버퍼로 만들어준다.

 

그리고 Item 클래스에 tostring 메서드 재정의를 해줘서 확인하기 편리하게 만들어줬다.

 

그리고 ActivityResultContracts 변수를 수정해주자

csv 파일을 읽고 받아온 item list를 for each 반복문을 돌면서 안에 들어있는 클래스를 log로 출력해줬다.

 

 

생각했던대로 제대로 나와줬다. 특별히 어려운것도 없고 안드로이드에서 쉽게 csv 파일을 불러오고 파싱할 수 있었다.

 

끝!

 

 

 

MainActivity.java 코드 전체

package com.example.myapplication;

import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.PathUtils;

import android.net.Uri;
import android.os.Bundle;
import android.util.Log;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class MainActivity extends AppCompatActivity {

    private List<Item> items = new ArrayList<Item>();   // csv 데이터를 이 리스트에 넣을거임

    private final ActivityResultLauncher<String> mGetContent = registerForActivityResult(
            new ActivityResultContracts.GetContent(),
            new ActivityResultCallback<Uri>() {
                @Override
                public void onActivityResult(Uri uri) {
                    // Handle the returned Uri
                    CSVParser csvParser = new CSVParser();
                    try {
                        items = csvParser.getItemList(MainActivity.this, uri);
                        items.forEach(new Consumer<Item>() {
                            @Override
                            public void accept(Item item) {
                                Log.d("csv", item.toString());
                            }
                        });

                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mGetContent.launch("text/*");



    }


}

 

Item.java 클래스 코드 전체

package com.example.myapplication;

public class Item {
    private int id;    // 고유 아이디
    private String date;    // 날짜
    private String name;    // 지역
    private String weather;    // 날씨
    private String fineDust;    // 미세먼지

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getWeather() {
        return weather;
    }

    public void setWeather(String weather) {
        this.weather = weather;
    }

    public String getFineDust() {
        return fineDust;
    }

    public void setFineDust(String fineDust) {
        this.fineDust = fineDust;
    }

    @Override
    public String toString() {
        return "Item{" +
                "id=" + id +
                ", date='" + date + '\'' +
                ", name='" + name + '\'' +
                ", weather='" + weather + '\'' +
                ", fineDust='" + fineDust + '\'' +
                '}';
    }
}
728x90
반응형