czytać plik z zasobów

178
public class Utils {
    public static List<Message> getMessages() {
        //File file = new File("file:///android_asset/helloworld.txt");
        AssetManager assetManager = getAssets();
        InputStream ims = assetManager.open("helloworld.txt");    
     }
}

Używam tego kodu, próbując odczytać plik z zasobów. Próbowałem to zrobić na dwa sposoby. Po pierwsze, gdy Fileotrzymałem użycie FileNotFoundException, gdy AssetManager getAssets()metoda nie jest rozpoznawana. Czy jest tu jakieś rozwiązanie?

ryba 40
źródło

Odpowiedzi:

225

Oto, co robię w ćwiczeniu polegającym na rozszerzaniu / modyfikowaniu buforowanego odczytu w celu dostosowania do twoich potrzeb

BufferedReader reader = null;
try {
    reader = new BufferedReader(
        new InputStreamReader(getAssets().open("filename.txt")));

    // do reading, usually loop until end of file reading  
    String mLine;
    while ((mLine = reader.readLine()) != null) {
       //process line
       ...
    }
} catch (IOException e) {
    //log the exception
} finally {
    if (reader != null) {
         try {
             reader.close();
         } catch (IOException e) {
             //log the exception
         }
    }
}

EDYCJA: Moja odpowiedź jest być może bezużyteczna, jeśli twoje pytanie dotyczy tego, jak to zrobić poza zajęciem. Jeśli twoje pytanie dotyczy po prostu sposobu odczytywania pliku z zasobu, odpowiedź jest powyżej.

AKTUALIZACJA :

Aby otworzyć plik określający typ, po prostu dodaj typ w wywołaniu InputStreamReader w następujący sposób.

BufferedReader reader = null;
try {
    reader = new BufferedReader(
        new InputStreamReader(getAssets().open("filename.txt"), "UTF-8")); 

    // do reading, usually loop until end of file reading 
    String mLine;
    while ((mLine = reader.readLine()) != null) {
       //process line
       ...
    }
} catch (IOException e) {
    //log the exception
} finally {
    if (reader != null) {
         try {
             reader.close();
         } catch (IOException e) {
             //log the exception
         }
    }
}

EDYTOWAĆ

Jak @Stan mówi w komentarzu, kod, który podaję, nie sumuje wierszy. mLinejest wymieniany przy każdym przejściu. Dlatego napisałem //process line. Zakładam, że plik zawiera jakieś dane (np. Listę kontaktów) i każda linia powinna być przetwarzana osobno.

W przypadku, gdy chcesz po prostu załadować plik bez żadnego przetwarzania, będziesz musiał podsumować mLinekażdy przebieg, używając StringBuilder()i dołączając każdy przebieg.

KOLEJNA EDYCJA

Zgodnie z komentarzem @Vincent dodałem finallyblokadę.

Należy również pamiętać, że w Javie 7 i górną można użyć try-with-resourcesdo używania AutoCloseablei Closeablewyposażony ostatniej Java.

KONTEKST

Za punkty komentarz @LunarWatcher się, że getAssets()jest classw context. Tak więc, jeśli wywołujesz to poza obiektem activity, musisz się do niego odwołać i przekazać wystąpienie kontekstu do działania.

ContextInstance.getAssets();

Jest to wyjaśnione w odpowiedzi @Maneesh. Więc jeśli jest ci to przydatne, zagłosuj za jego odpowiedzią, ponieważ to on wskazał na to.

HpTerm
źródło
2
@Stan, napisz o tym w komentarzach i pozwól autorowi zdecydować, czy chce go zaktualizować. Edycje służą poprawie przejrzystości, a nie zmianie znaczenia. Rewizje kodu powinny być zawsze publikowane najpierw jako komentarze.
KyleMit,
2
Twój kod nie gwarantuje zamknięcia strumienia i zwolnienia zasobu w odpowiednim czasie. Polecam skorzystać finally {reader.close();}.
Vincent Cantin
2
Myślę, że warto zwrócić uwagę, że powyższy kod pokazuje błąd w ADT - "reader.close ();" linia musi zostać umieszczona w innym bloku try-catch. Sprawdź ten wątek: stackoverflow.com/questions/8981589/… :)
JakeP
1
getAssets to klasa w Context, więc do użycia poza działaniem należy wywołać Context. Więc poza zajęciami będzie to coś w stylucontext.getAssets(.....)
Zoe,
1
Zgodnie z twoją aktualizacją (dzięki za dodanie tego btw), posiadanie kontekstu w statycznych polach to wyciek pamięci. Należy go używać ostrożnie i odpowiednio wyczyścić. W przeciwnym razie wystąpi wyciek pamięci, który może mieć duży wpływ na aplikację.
Zoe
65
getAssets()

działa tylko w Aktywności w innych klasach, których musisz użyć Context.

Stwórz konstruktor dla klasy Utils, przekazując odniesienie do aktywności (brzydki sposób) lub kontekst aplikacji jako parametr do niej. Używając tego użyj getAsset () w swojej klasie Utils.

user370305
źródło
1
Działa na wszystkim, co jest podklasą kontekstu, a jedną z wielu jest aktywność.
Jeremy Logan
Właśnie zauważyłem, że napisałem Context.
user370305
@ user370305 Czy wiesz, jak mogę przekonwertować InputStream na FileInputStream?
hotHead
@ user370305, w jaki sposób to jest brzydkie?
Sevastyan Savanyuk
Przekazywanie obiektów interfejsu użytkownika bez uwzględnienia konsekwencji jest ogólnie złą praktyką. Jeśli nie jesteś ostrożny, możesz spowodować wycieki pamięci lub spróbować użyć martwych kontekstów. Dobrze przeczytać na ten temat: android.jlelse.eu/memory-leak-patterns-in-android-4741a7fcb570
milosmns
49

Lepiej późno niż wcale.

W niektórych okolicznościach miałem trudności z odczytaniem plików wiersz po wierszu. Poniższa metoda jest najlepsza, jaką do tej pory znalazłem i polecam ją.

Stosowanie: String yourData = LoadData("YourDataFile.txt");

Gdzie zakłada się, że YourDataFile.txt znajduje się w aktywach /

 public String LoadData(String inFile) {
        String tContents = "";

    try {
        InputStream stream = getAssets().open(inFile);

        int size = stream.available();
        byte[] buffer = new byte[size];
        stream.read(buffer);
        stream.close();
        tContents = new String(buffer);
    } catch (IOException e) {
        // Handle exceptions here
    }

    return tContents;

 }
Florin Mircea
źródło
Mój ciąg zwrotny to android.content.res.AssetManager$AssetInputStream@4195dfa0 ..
Boldijar Paul
to samo tutaj, res.AssetManager $ AssetInputStream @ .... jakiś szczególny powód, dla którego zwraca to?
Bigs
Przydzielasz pamięć podwójnie - najpierw dla buforu, a następnie dla ciągu. Nie działa w przypadku większych plików.
JaakL
1
doskonały sposób na przydzielenie rozmiaru do bufora stream.available().
Kasim Rangwala
39
public String ReadFromfile(String fileName, Context context) {
    StringBuilder returnString = new StringBuilder();
    InputStream fIn = null;
    InputStreamReader isr = null;
    BufferedReader input = null;
    try {
        fIn = context.getResources().getAssets()
                .open(fileName, Context.MODE_WORLD_READABLE);
        isr = new InputStreamReader(fIn);
        input = new BufferedReader(isr);
        String line = "";
        while ((line = input.readLine()) != null) {
            returnString.append(line);
        }
    } catch (Exception e) {
        e.getMessage();
    } finally {
        try {
            if (isr != null)
                isr.close();
            if (fIn != null)
                fIn.close();
            if (input != null)
                input.close();
        } catch (Exception e2) {
            e2.getMessage();
        }
    }
    return returnString.toString();
}
swathi
źródło
Można by pomyśleć, że jeśli zamkniesz BufferedReader, będzie musiał automatycznie zamknąć również InputStreanReader i InputStream. Bo co z tego nie tworzysz klamki do tych np input = new BufferedReader(new InputStreamReader(fIn));.
trans
3
Sugerowałbym utworzenie oddzielnych bloków try / catch w celu zamknięcia wszystkich zasobów na końcu; zamiast wrzucać je wszystkie w jeden - ponieważ może to pozostawić inne zasoby niezamknięte, jeśli wcześniejsza próba zamknięcia innego zasobu zgłosi wyjątek.
Reece
9
AssetManager assetManager = getAssets();
InputStream inputStream = null;
try {
    inputStream = assetManager.open("helloworld.txt");
}
catch (IOException e){
    Log.e("message: ",e.getMessage());
}
Siva Charan
źródło
8

rozwiązanie z jedną linią dla kotlin:

fun readFileText(fileName: String): String {
    return assets.open(fileName).bufferedReader().use { it.readText() }
}
Przetrząsać
źródło
7

getAssets() zadziała, gdy będziesz wywoływać wewnątrz klasy Activity.

Jeśli wywołujesz tę metodę w klasie innej niż Activity, musisz wywołać tę metodę z Context, która jest przekazywana z klasy Activity. Poniżej znajduje się linia, przez którą można uzyskać dostęp do metody.

ContextInstance.getAssets();

ContextInstance można zaliczyć jako tę z klasy aktywności.

Maneesh
źródło
5

Czytanie i pisanie plików zawsze było rozwlekłe i podatne na błędy. Unikaj tych odpowiedzi i zamiast tego użyj Okio :

public void readLines(File file) throws IOException {
  try (BufferedSource source = Okio.buffer(Okio.source(file))) {
    for (String line; (line = source.readUtf8Line()) != null; ) {
      if (line.contains("square")) {
        System.out.println(line);
      }
    }
  }
}
Saket
źródło
1
Czy wiesz, dlaczego wygląda to bardziej estetycznie i krótko? Cóż, ponieważ pominąłeś tu przynajmniej połowę kodu. Pominięte części: 1) IOExceptionblok try / catch 2) zamykanie strumieni w przypadku wyrzucenia wyjątku 3) ten kod czyta pojedynczą linię, a nie cały plik. Pod względem wydajności ta biblioteka jest zdecydowanie jedyna w swoim rodzaju, nie ma co do tego wątpliwości. Teraz powiedz mi, czy nadal powinienem unikać „tych odpowiedzi” i stosować Okio tylko do odczytu plików? Odpowiedź brzmi NIE, chyba że jest to już część Twojej aplikacji.
Farid
Zaktualizowałem moją odpowiedź.
Saket
4

Oto metoda odczytu pliku w zasobach:

/**
 * Reads the text of an asset. Should not be run on the UI thread.
 * 
 * @param mgr
 *            The {@link AssetManager} obtained via {@link Context#getAssets()}
 * @param path
 *            The path to the asset.
 * @return The plain text of the asset
 */
public static String readAsset(AssetManager mgr, String path) {
    String contents = "";
    InputStream is = null;
    BufferedReader reader = null;
    try {
        is = mgr.open(path);
        reader = new BufferedReader(new InputStreamReader(is));
        contents = reader.readLine();
        String line = null;
        while ((line = reader.readLine()) != null) {
            contents += '\n' + line;
        }
    } catch (final Exception e) {
        e.printStackTrace();
    } finally {
        if (is != null) {
            try {
                is.close();
            } catch (IOException ignored) {
            }
        }
        if (reader != null) {
            try {
                reader.close();
            } catch (IOException ignored) {
            }
        }
    }
    return contents;
}
Jared Rummler
źródło
To dobra odpowiedź, ale użycie konkatenacji ciągów jest złym podejściem. Zamiast tego rozważ użycie StringBuilder. StringBuilder contentBuilder = new StringBuilder (); while ((line = reader.readLine ())! = null) {builder.append ("\ n"). append (line); } I na koniec możesz stworzyć nowy obiekt String w ten sposób: content = contentBuilder.toString ();
Barterio,
4

Możesz załadować zawartość z pliku. Weź pod uwagę, że plik znajduje się w folderze zasobów.

public static InputStream loadInputStreamFromAssetFile(Context context, String fileName){
    AssetManager am = context.getAssets();
    try {
        InputStream is = am.open(fileName);
        return is;
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

public static String loadContentFromFile(Context context, String path){
    String content = null;
    try {
        InputStream is = loadInputStreamFromAssetFile(context, path);
        int size = is.available();
        byte[] buffer = new byte[size];
        is.read(buffer);
        is.close();
        content = new String(buffer, "UTF-8");
    } catch (IOException ex) {
        ex.printStackTrace();
        return null;
    }
    return content;
}

Teraz możesz pobrać zawartość, wywołując funkcję w następujący sposób

String json= FileUtil.loadContentFromFile(context, "data.json");

Biorąc pod uwagę, że data.json jest przechowywana w Application \ app \ src \ main \ asset \ data.json

Siddarth Kanted
źródło
3

W MainActivity.java

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

        TextView tvView = (TextView) findViewById(R.id.tvView);

        AssetsReader assetsReader = new AssetsReader(this);
        if(assetsReader.getTxtFile(your_file_title)) != null)
        {
            tvView.setText(assetsReader.getTxtFile(your_file_title)));
        }
    }

Możesz także utworzyć oddzielną klasę, która wykonuje całą pracę

public class AssetsReader implements Readable{

    private static final String TAG = "AssetsReader";


    private AssetManager mAssetManager;
    private Activity mActivity;

    public AssetsReader(Activity activity) {
        this.mActivity = activity;
        mAssetManager = mActivity.getAssets();
    }

    @Override
    public String getTxtFile(String fileName)
    {
        BufferedReader reader = null;
        InputStream inputStream = null;
        StringBuilder builder = new StringBuilder();

        try{
            inputStream = mAssetManager.open(fileName);
            reader = new BufferedReader(new InputStreamReader(inputStream));

            String line;

            while((line = reader.readLine()) != null)
            {
                Log.i(TAG, line);
                builder.append(line);
                builder.append("\n");
            }
        } catch (IOException ioe){
            ioe.printStackTrace();
        } finally {

            if(inputStream != null)
            {
                try {
                    inputStream.close();
                } catch (IOException ioe){
                    ioe.printStackTrace();
                }
            }

            if(reader != null)
            {
                try {
                    reader.close();
                } catch (IOException ioe)
                {
                    ioe.printStackTrace();
                }
            }
        }
        Log.i(TAG, "builder.toString(): " + builder.toString());
        return builder.toString();
    }
}

Moim zdaniem lepiej jest stworzyć interfejs, ale nie jest to konieczne

public interface Readable {
    /**
     * Reads txt file from assets
     * @param fileName
     * @return string
     */
    String getTxtFile(String fileName);
}
Volodymyr Shalashenko
źródło
2

Jeśli używasz innej klasy niż Aktywność, możesz chcieć zrobić coś podobnego,

BufferedReader bufferedReader = new BufferedReader(new InputStreamReader( YourApplication.getInstance().getAssets().open("text.txt"), "UTF-8"));
Zhaolong Zhong
źródło
2

Korzystając z Kotlin, możesz wykonać następujące czynności, aby odczytać plik z zasobów w systemie Android:

try {
    val inputStream:InputStream = assets.open("helloworld.txt")
    val inputString = inputStream.bufferedReader().use{it.readText()}
    Log.d(TAG,inputString)
} catch (e:Exception){
    Log.d(TAG, e.toString())
}
Vamsi Tallapudi
źródło
2

Może za późno, ale ze względu na innych, którzy szukają brzoskwiniowych odpowiedzi:

public static String loadAssetFile(Context context, String fileName) {
    try {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(context.getAssets().open(fileName)));
        StringBuilder out= new StringBuilder();
        String eachline = bufferedReader.readLine();
        while (eachline != null) {
            out.append(eachline);
            eachline = bufferedReader.readLine();
        }
        return out.toString();
    } catch (IOException e) {
        Log.e("Load Asset File",e.toString());
    }
    return null;
}
ucMedia
źródło
1

cityfile.txt

   public void getCityStateFromLocal() {
        AssetManager am = getAssets();
        InputStream inputStream = null;
        try {
            inputStream = am.open("city_state.txt");
        } catch (IOException e) {
            e.printStackTrace();
        }
        ObjectMapper mapper = new ObjectMapper();
        Map<String, String[]> map = new HashMap<String, String[]>();
        try {
            map = mapper.readValue(getStringFromInputStream(inputStream), new TypeReference<Map<String, String[]>>() {
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
        ConstantValues.arrayListStateName.clear();
        ConstantValues.arrayListCityByState.clear();
        if (map.size() > 0)
        {
            for (Map.Entry<String, String[]> e : map.entrySet()) {
                CityByState cityByState = new CityByState();
                String key = e.getKey();
                String[] value = e.getValue();
                ArrayList<String> s = new ArrayList<String>(Arrays.asList(value));
                ConstantValues.arrayListStateName.add(key);
                s.add(0,"Select City");
                cityByState.addValue(s);
                ConstantValues.arrayListCityByState.add(cityByState);
            }
        }
        ConstantValues.arrayListStateName.add(0,"Select States");
    }
 // Convert InputStream to String
    public String getStringFromInputStream(InputStream is) {
        BufferedReader br = null;
        StringBuilder sb = new StringBuilder();
        String line;
        try {
            br = new BufferedReader(new InputStreamReader(is));
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return sb + "";

    }
tej shah
źródło
Link teraz powracaFile no longer available That file has now been permanently removed and cannot be recovered
gregn3
1

Klasa Scanner może to uprościć.

        StringBuilder sb=new StringBuilder();
        Scanner scanner=null;
        try {
            scanner=new Scanner(getAssets().open("text.txt"));
            while(scanner.hasNextLine()){
                sb.append(scanner.nextLine());
                sb.append('\n');
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(scanner!=null){try{scanner.close();}catch (Exception e){}}
        }
        mTextView.setText(sb.toString());
Solomon P. Byer
źródło
Możesz połączyć te 2 linie sb.append (scanner.nextLine ()); sb.append ('\ n'); to sb.appendln (scanner.nextLine ());
Mojtaba
0

@HpTerm odpowiedz Wersja Kotlin:

private fun getDataFromAssets(): String? {

    var bufferedReader: BufferedReader? = null
    var data: String? = null

    try {
        bufferedReader = BufferedReader(
            InputStreamReader(
                activity?.assets?.open("Your_FILE.html"),     
                "UTF-8"
            )
        )                  //use assets? directly if in activity

       var mLine:String = bufferedReader?.readLine()
        while (mLine != null) {
            data+= mLine
            mLine=bufferedReader.readLine()
        }

    } catch (e: Exception) {
        e.printStackTrace()
    } finally {
        try {
            bufferedReader?.close()
        } catch (e: Exception) {
           e.printStackTrace()
        }
    }
    return data
}
Manohar Reddy
źródło
Obecnie to doda null do ciągu. Potrzebne zmiany: var mLine:Stringpowinno być var mLine:String? var data: String?powinno być var data = ""zwracanym typemString
fupduck
0

Oto sposób, aby uzyskać InputStreamdla pliku w assetsfolderze bez Context, Activity, Fragmentlub Application. To od Ciebie InputStreamzależy, w jaki sposób uzyskasz dane . W innych odpowiedziach jest na to mnóstwo sugestii.

Kotlin

val inputStream = ClassLoader::class.java.classLoader?.getResourceAsStream("assets/your_file.ext")

Jawa

InputStream inputStream = ClassLoader.class.getClassLoader().getResourceAsStream("assets/your_file.ext");

Wszystkie zakłady są wyłączone, jeśli ClassLoaderw grze jest zwyczaj .

bartonstanley
źródło