안드로이드 텍스트(text) 파일 입출력 2. [Example] (Android Text File I/O 2)

2017. 2. 27. 14:08


1. 안드로이드에서 텍스트(text) 파일 읽고 쓰기.

안드로이드 앱에서, 사용자로부터 텍스트를 입력받아 화면에 표시하는 것은 매우 흔한 일입니다. 입력받은 텍스트는 앱 내부에서 가공되어 변형된 형태로 표시되기도 하고, 또는 다른 여러 화면에서 재사용되기도 하죠. 그런데 화면에 표시되는 텍스트 데이터를 메모리 변수에만 저장하고 있으면, 앱이 종료될 때 그 데이터가 모두 사라지게 됩니다.


그래서 앱 종료 후에도 계속적으로 유지되어야 할 데이터는 반드시 어딘가에 저장해둔 다음, 앱이 다시 실행될 때, 저장된 데이터를 로드하여 사용할 수 있도록 만들어야 합니다. 이 때 데이터를 저장하기 위해 개발자가 선택할 수 있는 가장 손 쉬운 방법은 바로 파일을 사용하는 것입니다. 안드로이드 기기 내부 저장소(Internal Storage)에서 앱이 접근할 수 있는 디렉토리 하위에 파일을 생성한 다음, 파일 입출력 API 함수를 통해 데이터를 저장하거나 로드할 수 있죠.


바이너리(binary) 데이터를 파일에 읽고 쓰는 방법에 대해서는 [안드로이드 바이너리(binary) 파일 입출력 1]에서 그 개념과 절차를 설명하고, [안드로이드 바이너리(binary) 파일 입출력 2]에서 실질적인 예제 코드를 살펴보았습니다. 그리고 텍스트 파일에 대한 입출력 방법은 [안드로이드 텍스트(text) 파일 입출력 1]에서 설명하였습니다.


그럼 이제, 간단한 앱 예제 작성을 통해 안드로이드에서 텍스트 데이터를 파일에 읽고 쓰는 방법에 대해 알아보도록 하겠습니다.

2. 안드로이드 파일 입출력 하기에 앞서 알아두어야 할 사항.

2.1 안드로이드 앱에서 파일을 일고 쓸 수 있는 디렉토리 경로.

안드로이드 앱에서 파일을 읽고 쓰기 위해서는, 해당 디렉토리에 대한 읽기 권한 또는 쓰기 권한이 허용되어야 합니다. 만약 권한이 없어도 마음대로 파일을 읽고 쓸 수 있다면, 앱이 가진 고유한 데이터가 유출되거나 시스템 파일을 마음대로 바꿀 수 있게 되어 제대로 된 시스템 운영을 할 수 없게 되죠.


[안드로이드 바이너리(binary) 파일 입출력 2]에서 설명했듯이, 안드로이드 앱이 기본적으로 읽고 쓸 수 있는 디렉토리 경로는 "/data/user/[USER-NO]/[PACKAGE]/files/"(또는 "/data/data/[PACKAGE-NAME]/files/") 입니다. 하지만 직접 전체 경로 이름을 지정하여 파일에 접근하는 것은 바람직하지 않고, 대신 Context(android.content.Context)의 getFilesDir() 함수를 사용하여 해당 경로를 얻어와야 합니다.

    File file = new File(getFilesDir(), FILENAME) ;

2.2 줄(line) 단위로 읽기, 줄 바꿈 쓰기.

바이트(byte) 단위로 처리되는 바이너리 데이터는, 각 바이트 값이 사용되는데 있어 딱히 용도가 구분되어 있지 않기 때문에, "XX 값이 나오기 전까지의 데이터를 읽는 함수"가 존재하지 않습니다. 그래서 데이터를 읽는 read() 함수에는 반드시 읽어들일 데이터의 크기를 명시하게 되어 있죠.

    int read() ;                           // 1 바이트 데이터 읽기.
    int read(byte[] b, int off, int len) ; // off 위치에서 len 만큼 b 버퍼에 읽기.
    int read(byte[] b) ;                   // b.length만큼 b 버퍼에 읽기.

하지만 문자(char) 단위로 처리되는 텍스트 데이터는, 화면에 문자로 표시되는 값 외에 "줄 바꿈" 이라는 조금은 특수한(?) 용도의 값이 구분되어 있어서, "'줄 바꿈'문자가 나올 때까지 데이터를 읽는 함수"가 존재합니다. 즉, 고정된 버퍼 크기가 아닌, 가변 길이의 줄 단위로 데이터를 읽는 것이 가능하다는 것이며, 이는 BufferedReader 클래스의 readLine() 함수가 그 역할을 수행합니다.

    String readLine() ;     // 한 줄 텍스트 데이터를 읽어 String 타입으로 리턴.

또한 "줄 바꿈" 문자는 텍스트 데이터를 쓰는 경우에도 유사하게 적용될 수 있습니다. 텍스트 데이터를 파일에 쓴 다음 임의의 줄을 바꿀 때, BufferedWriter 클래스의 newLine() 함수를 사용하면 됩니다.

    void newLine() ;        // 줄 바꿈 문자 쓰기.

3. 안드로이드 텍스트(text) 파일 입출력 예제.

이제 예제 앱 작성을 통해, 안드로이드에서 텍스트 파일을 읽고 쓰는 방법을 살펴보겠습니다.


예제의 화면은 아래 그림과 같이 구성됩니다.

파일 입출력 예제 화면 구성도


예제 화면의 EditText에 문자열을 입력하고, "ADD" 버튼을 누르면 입력된 문자열은 리스트뷰에 추가됩니다. 그리고 동시에 리스트뷰의 모든 항목은 파일에 저장됩니다.


ListView의 아이템을 하나 선택하고 "Del" 버튼을 선택하는 경우도 마찬가지입니다. 선택된 아이템은 ListView에서 삭제되고, 동시에 리스트뷰의 모든 항목은 파일에 저장됩니다.


마지막으로 앱을 종료한 다음 재실행하면, 파일에 저장된 텍스트 데이터를 읽어들여 리스트뷰에 표시합니다.

3.1 MainActivity의 Layout 구성.

예제 앱을 작성하기 위해 첫 번째로 해야 할 일은 MainActivity의 Layout을 작성하는 것입니다. 앞서 설계한 예제 화면의 구성도에 따라 아래와 같이 XML 코드를 작성합니다.

[STEP-1] "activity_main.xml" - MainActivity의 Layout 구성.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/content_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.recipes4dev.examples.filetextioexample1.MainActivity"
    tools:showIn="@layout/activity_main">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <ListView
            android:id="@+id/listview1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:choiceMode="singleChoice" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <EditText
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:id="@+id/editTextNew"/>

            <Button
                android:id="@+id/buttonAdd"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginRight="10dp"
                android:text="Add" />

            <Button
                android:id="@+id/buttonDel"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Delete" />

        </LinearLayout>

    </LinearLayout>
</RelativeLayout>

3.2 파일 이름과 리스트뷰를 위한 클래스 변수 선언.

다음, 리스트뷰를 사용하기 위한 변수들과, 파일이름을 위한 상수를 선언합니다.

[STEP-2] "MainActivity.java" - MainActivity에 변수 추가.
public class MainActivity extends AppCompatActivity {
    private final String fileName = "items.list" ;

    private ListView listview ;
    private ArrayAdapter adapter ;
    private ArrayList<String> items = new ArrayList<String>() ;

    // ... 코드 계속

}

3.3 리스트뷰와 어댑터 초기화.

이제 MainActivity의 onCreate() 함수에서 리스트뷰와 어댑터를 초기화 합니다.

[STEP-3] "MainActivity.java" - onCreate() 함수에서 리스트뷰 초기화.
public class MainActivity extends AppCompatActivity {

    // 코드 계속 ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        
        // 코드 계속 ...
        listview = (ListView) findViewById(R.id.listview1) ;
        adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_single_choice, items) ;

        listview.setAdapter(adapter) ;

        // ... 코드 계속
    }
}

3.4 리스트뷰 아이템을 파일에 저장하는 함수 작성.

리스트뷰의 아이템이 추가 또는 삭제되면, 리스트뷰의 아이템을 파일에 저장하도록 만듭니다. 이는 saveItemsToFile() 이라는 함수로 만들어놓고, 각 버튼의 클릭 이벤트 핸들러에서 호출해서 사용합니다. 특히 리스트뷰의 아이템에는 "줄 바꿈" 문자가 포함되어 있지 않기 때문에, 아이템을 줄 단위로 저장하기 위해 문자열 저장 후 newLine() 함수를 호출한 것을 확인하시기 바랍니다.

[STEP-4] "MainActivity.java" - 리스트뷰 아이템을 파일에 저장하는 saveItemsToFile() 추가 및 작성.
public class MainActivity extends AppCompatActivity {

    // 코드 계속 ...

    private void saveItemsToFile() {
        File file = new File(getFilesDir(), fileName) ;
        FileWriter fw = null ;
        BufferedWriter bufwr = null ;

        try {
            // open file.
            fw = new FileWriter(file) ;
            bufwr = new BufferedWriter(fw) ;
            
            for (String str : items) {
                bufwr.write(str) ;
                bufwr.newLine() ;
            }

            // write data to the file.
            bufwr.flush() ;

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

        try {
            // close file.
            if (bufwr != null) {
                bufwr.close();
            }

            if (fw != null) {
                fw.close();
            }
        } catch (Exception e) {
            e.printStackTrace() ;
        }
    }
}

3.5 리스트뷰 아이템을 파일로부터 읽어들이는 함수 작성.

앱 시작 시, 파일로부터 줄 단위의 텍스트 데이터를 읽어들여, 리스트뷰에 표시하는 기능을 수행하는 함수를 작성합니다. 아이템을 저장할 때 줄 단위로 아이템을 구분하였으므로, 한 줄의 텍스트를 읽기 위해 readLine() 함수를 사용한 것에 주의하세요.

[STEP-5] "MainActivity.java" - 파일로부터 리스트뷰 아이템을 읽어들이는 loadItemsFromFile() 추가 및 작성.
public class MainActivity extends AppCompatActivity {

    // 코드 계속 ...

    private void loadItemsFromFile() {
        File file = new File(getFilesDir(), fileName) ;
        FileReader fr = null ;
        BufferedReader bufrd = null ;
        String str ;
        
        if (file.exists()) {
            try {
                // open file.
                fr = new FileReader(file) ;
                bufrd = new BufferedReader(fr) ;

                while ((str = bufrd.readLine()) != null) {
                    items.add(str) ;
                }

                bufrd.close() ;
                fr.close() ;
            } catch (Exception e) {
                e.printStackTrace() ;
            }
        }
    }

    // ... 코드 계속
}

3.6 앱 시작 시, 파일에서 데이터 로딩하여 리스트뷰에 표시하기.

마지막으로, 앱 시작 시, 최종적으로 파일에 저장된 데이터를 읽어들여 리스트뷰에 표시되도록 만듭니다.

[STEP-6] "MainActivity.java" - 앱 시작 시, onCreate() 함수에서 파일 내용 읽어들여 리스트뷰에 표시.
public class MainActivity extends AppCompatActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        
        // 코드 계속 ...

        // 파일에서 데이터를 읽어들여 리스트뷰에 표시.
        loadItemsFromFile() ;
        adapter.notifyDataSetChanged();

        // ... 코드 계속
    }
}

3.7 아이템 추가(Add) 버튼 클릭 이벤트 처리

앱을 실행하면 최초에 아이템 추가(Add) 버튼은 비활성(disabled) 상태로 표시됩니다. 그리고 앱의 에디트텍스트에 텍스트가 입력되면, 아이템 추가(Add) 버튼은 활성(enabled) 상태로 변경됩니다.
활성 상태로 변경된 아이템 추가(Add) 버튼이 눌려지면, 에디트텍스트에 입력된 문자열을 리스트뷰에 추가하고 파일에 그 내용을 갱신합니다.

[STEP-7] "MainActivity.java" - onCreate() 함수에서 아이템 추가(Add) 버튼 이벤트 처리.
public class MainActivity extends AppCompatActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        
        // 코드 계속 ...

        Button buttonAdd = (Button) findViewById(R.id.buttonAdd) ;
        buttonAdd.setEnabled(false) ; // 초기 버튼 상태 비활성 상태로 지정.
        buttonAdd.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
                EditText editTextNew = (EditText) findViewById(R.id.editTextNew) ;
                String strNew = (String) editTextNew.getText().toString() ;

                if (strNew.length() > 0) {
                    // 리스트에 문자열 추가.
                    items.add(strNew);

                    // 에디트텍스트 내용 초기화.
                    editTextNew.setText("") ;

                    // 리스트뷰 갱신
                    adapter.notifyDataSetChanged();

                    // 리스트뷰 아이템들을 파일에 저장.
                    saveItemsToFile() ;
                }
            }
        });

        // ... 코드 계속
    }
}

3.8 아이템 삭제(Del) 버튼 클릭 이벤트 처리

리스트뷰에서 아이템을 선택하고 아이템 삭제(Del) 버튼을 누르면, 선택된 아이템을 리스트뷰에서 삭제하고 파일에 반영하도록 구현합니다.

[STEP-8] "MainActivity.java" - onCreate() 함수에서 아이템 삭제(Del) 버튼 이벤트 처리.
public class MainActivity extends AppCompatActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        
        // 코드 계속 ...

        Button buttonDel = (Button) findViewById(R.id.buttonDel) ;
        buttonDel.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
                int count ;
                int checkedIndex ;

                count = adapter.getCount() ;

                if (count > 0) {
                    // 리스트뷰에서 선택된 아이템 인덱스 얻어오기.
                    checkedIndex = listview.getCheckedItemPosition();
                    if (checkedIndex > -1 && checkedIndex < count) {
                        // 아이템 삭제
                        items.remove(checkedIndex) ;

                        // 리스트뷰 선택 초기화.
                        listview.clearChoices();

                        // 리스트뷰 갱신
                        adapter.notifyDataSetChanged();

                        // 리스트뷰 아이템들을 파일에 저장.
                        saveItemsToFile() ;
                    }
                }
            }
        });

        // ... 코드 계속
    }
}

3.9 입력 필드 내용 변경 시, 아이템 추가(Add) 버튼 상태 변경.

화면에 배치된 입력 필드인 에디트텍스트의 내용이 변경되면, 아이템 추가(Add) 버튼의 상태를 변경하는 코드를 작성합니다. 이 때, 에디트텍스트에 텍스트가 존재하면 버튼의 상태를 활성(enabled) 상태로 만들고, 텍스트가 비어 있으면 비활성(disabled) 상태로 만듭니다.

[STEP-9] "MainActivity.java" - onCreate() 함수에서 에디트텍스트 내용 변경 이벤트 처리.
public class MainActivity extends AppCompatActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        
        // 코드 계속 ...

        EditText editTextNew = (EditText) findViewById(R.id.editTextNew) ;
        editTextNew.addTextChangedListener(new TextWatcher() {
            @Override
            public void afterTextChanged(Editable edit) {
                Button buttonAdd = (Button) findViewById(R.id.buttonAdd) ;
                if (edit.toString().length() > 0) {
                    // 버튼 상태 활성화.
                    buttonAdd.setEnabled(true) ;
                } else {
                    // 버튼 상태 비활성화.
                    buttonAdd.setEnabled(false) ;
                }
            }

            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
            }
        }) ;

        // ... 코드 계속
    }
}

4. 예제 실행 화면.

예제 코드를 작성한 다음 앱을 실행하면, 아래와 같은 화면이 표시됩니다.

파일 입출력 예제 실행 화면 1


화면의 에디트텍스트에 추가할 텍스트를 입력하고 "Add" 버튼을 누르면 입력한 텍스트가 리스트뷰에 표시됩니다.

파일 입출력 예제 실행 화면 2


여러 개의 아이템을 추가한 다음, 앱을 종료하고 다시 실행해도 리스트뷰에는 마지막 아이템들이 그대로 표시됩니다. 이는 삭제의 경우에도 마찬가지입니다.

파일 입출력 예제 실행 화면 3


5. 참고.

.END.


ANDROID 프로그래밍/FILE , , , , , , , , , ,

  1. Blog Icon
    질문요

    제가 지금 임시로 파일을 만들어서 데이터를 저장하고 있는데요. 만약 파일의 내용을 지우려면 어떻게 해야할까요? 파일 자체를 지우는 건 아니고 파일의 내용을 전부 없애고 싶거든요. 찾아봐도 방법을 모르겠어서 질문드립니다.

  2. 파일의 내용을 지우는 방법은 파일을 삭제한 다음, 빈 파일을 새로 만들면 될 것 같습니다.

    파일을 지우고 빈 파일을 만드는 방법은 File 클래스의 delete() 함수와 createNewFile() 함수를 사용하시면 됩니다.
    관련 내용은 "https://developer.android.com/reference/java/io/File.html" 에 가보시면 잘 설명되어 있습니다.
    사용 방법이 어렵지 않으니 쉽게 쓸 수 있을 것 같네요.

    다른 질문 내용 있으시면 글 남겨주세요.
    감사합니다.

  3. Blog Icon

    비밀댓글입니다

  4. 올려주신 코드의 내용을 보면, 다른 예제의 내용과 섞어서 작성하신 거 같은데요.

    음, 본문의 예제 프로젝트는 현재 보내드리기가 쉽지 않을 거 같구요. 대신, 직접 예제를 한번 그대로 작성해보시는 게 좋을 거 같다는 생각이 드네요.

    본문의 예제에서 굵게(bold) 표시된 내용만 작성해보시면 그 실행 결과를 쉽게 확인하실 수 있을 것입니다.

    일단 만들어보신 다음, 잘 안되거나 문제가 생기면 다시 글 남겨주세요.

    감사합니다.

  5. Blog Icon

    비밀댓글입니다

  6. 네. 본문의 리스트뷰 대신 그리드뷰로 표시되도록 만들 수 있습니다.
    블로그에는 그리드뷰 관련 내용이 없지만, 조금만 검색해보시면 쉽게 찾으실 수 있을 거에요.

  7. Blog Icon
    상어

    안녕하세요. 덕분에 요즘 열심히 안드로이드 스튜디오 공부하고 있습니다.
    리스트뷰를 체크박스로 구성하고 파일에 저장하고싶은데 어떻게 하면 좋을까요?
    또, 버튼을 누르면 다른 액티비티로 넘어가서 내용을 입력 받고 리스트뷰에 추가하고 싶은데 이 경우에는 다른 액티비티에서 본문 3.4를 입력하면 되나요??

  8. 음, 질문에 대한 답변을 달기엔, 답글 공간이 부족한 것 같네요.

    리스트뷰와 액티비티에 관련된 내용을 조금 더 살펴보시고, 조금만 더 구체적이고, 좁은 범위의 질문을 부탁드릴게요.

    체크박스로 구성하는 것과 파일에 저장하는 것은 별개의 문제니까, 블로그의 리스트뷰 관련 내용(http://recipes4dev.tistory.com/category/ANDROID%20%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D/LISTVIEW)과 파일 처리(http://recipes4dev.tistory.com/category/ANDROID%20%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D/FILE) 부분을 살펴보시기 바랍니다.

    그리고 액티비티는 "http://recipes4dev.tistory.com/83?category=660156" 링크의 내용을 확인하시면 도움될 것 같습니다.

    감사합니다.

  9. Blog Icon
    아이작

    뽀따님의 게시글중 선택기능을 가진 커스텀리스트뷰 제작시에는

    arraylist 선언을 adapter 클래스에서 해주셨는데

    이 게시글과 같은 경우 메인액티비티 클래스에서 선언하셨는데 이럴 경우

    저장 함수 및 불러오기 함수를 adapter 클래스에서 정의해 주어야 하나요?

  10. 음.. 질문에 대한 답은...
    "하고 싶은 곳에서 하세요!!!"
    입니다.
    하지만.. 이렇게만 답을 드리면 "어쩌란 거지?"라고 하시겠죠.. ^^;;

    정확히 표현해드리자면, 리스트뷰에 데이터를 표시하기 위해 데이터리스트(ArrayList 객체)를 어디에서 만들 것인지는 중요하지 않습니다.

    MainActivity에서 만들 수도 있고, Adapter가 가지도록 만들 수도 있고, 아니면 또 다른 싱글톤 객체에서 만들 수도 있습니다.

    중요한 것은!!!
    어댑터가 데이터리스트(ArrayList 객체)의 참조를 가지고 리스트 아이템 뷰를 생성할 수 있도록 만들어주기만 하면 된다는 사실입니다.

    메인 액티비티에서 데이터리스트를 생성했다면, 보통 어댑터의 생성자를 통해 데이터리스트의 참조(본문의 예제에서는 items)를 전달하여 어댑터가 데이터리스트를 참조할 수 있도록 만들어줍니다.

    그렇지 않고 "선택기능을 지원하는 커스텀 리스트뷰 만드는 방법(https://recipes4dev.tistory.com/68)"에서 처럼 어댑터에 데이터리스트를 만든 경우에는 addItem()과 같은 메서드를 작성하여 데이터리스트에 값을 추가할 수 있도록 만들어주죠. (물론 addItem() 같은 메서드를 작성할 것인지도 개발자의 선택입니다.)

    물론, 어댑터의 주 역할이 데이터를 뷰로 변경하는 것이기 때문에 데이터 값의 저장과 불러오기 등은 어댑터 밖에서 해주는 게 원칙적으로 맞는 내용이긴 하죠.

    어쨌든, 데이터리스트의 객체를 어디서 생성하느냐가 중요한 게 아니라는 것! 중요한 것은 어댑터가 데이터리스트를 참조할 수 있게만 만들어주면 된다는 것! 기억하시기 바랍니다.

    답변이 도움이 되셨나요?

    잘 이해가 안되면 다시 질문글 남겨주세요.

    감사합니다.

  11. Blog Icon
    아이작

    뽀따님의 내용에 대해서 50%정도의 해답을 얻었습니다.

    한번 프로젝트를 엎고 메인액티비티에서 참조 및 새로운 생성을 통하여 예전과 같도록 다시 작업하여서 성공하였습니다.

    하지만 완성하고 보니 다시 문제가 발생하였고 고민을 해보았지만 아직 답을 찾지 못했습니다.

    기본적으로

    뽀따님의 프로젝트 예제같은 경우 하나의 텍스트뷰를 가진 아이템이였고 제가 구현 중인 커스텀리스트뷰 같은경우 텍스트뷰를 두개를 사용하게 됩니다.

    문제는
    save 함수 및 load 함숙에서 발생했습니다.

    내용을 고치고 있지만

    try {
    // open file.
    fw = new FileWriter(file) ;
    bufwr = new BufferedWriter(fw) ;

    for (String str : items) {
    bufwr.write(str) ;
    bufwr.newLine() ;
    }

    이부분에서 마지막 for문에서 이해가 잘되지 않았습니다.

    Arraylist를 받아와서 String str : items 이게 무슨 말인지 감이 잘 잡히지 않습니다.
    혹시 설명해 주실수있나요?

  12. 음, 이 질문에 대한 해답은 스스로 찾으셔야 할 것 같습니다.

    자바 for 루프에서 Collections 또는 Arrays 타입을 탐색하는 방법인데, 이 내용까지 하나하나 설명하게 되면 끝이 없을 것 같아서요.

    그리 어려운 내용이 아니니깐, 자바 관련 참고서적 또는 사이트를 참고하시면 쉽게 이해할 수 있을거라 생각됩니다.

    참고로, 아래 내용은 오라클에서 제공하는 튜토리얼에 포함된 내용입니다. (https://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html)


    =========================
    The for statement also has another form designed for iteration through Collections and arrays This form is sometimes referred to as the enhanced for statement, and can be used to make your loops more compact and easy to read. To demonstrate, consider the following array, which holds the numbers 1 through 10:

    int[] numbers = {1,2,3,4,5,6,7,8,9,10};
    The following program, EnhancedForDemo, uses the enhanced for to loop through the array:


    class EnhancedForDemo {
    public static void main(String[] args){
    int[] numbers =
    {1,2,3,4,5,6,7,8,9,10};
    for (int item : numbers) {
    System.out.println("Count is: " + item);
    }
    }
    }
    In this example, the variable item holds the current value from the numbers array. The output from this program is the same as before:

    Count is: 1
    Count is: 2
    Count is: 3
    Count is: 4
    Count is: 5
    Count is: 6
    Count is: 7
    Count is: 8
    Count is: 9
    Count is: 10
    We recommend using this form of the for statement instead of the general form whenever possible.