I have a large text file that is in the assets folder, and I have a button that reads the file's next line successfully. However, I want to read the previous line in case the user clicks another button.
Reading the whole file to memory is not an option. The file lines are not numbered.
InputStream is = getResources().getAssets().open("abc.txt");
String result= convertStreamToString(is);
public static String convertStreamToString(InputStream is)
throws IOException {
Writer writer = new StringWriter();
char[] buffer = new char[2048];
try {
Reader reader = new BufferedReader(new InputStreamReader(is,
"UTF-8"));
int n;
while ((n = reader.read(buffer)) != -1) {
writer.write(buffer, 0, n);
}
} finally {
is.close();
}
String text = writer.toString();
return text;
}
If you only need to keep track of one previous line, you can do something like the following, keeping track of the previous line through each iteration (I assumed you were using a reader; for this example, BufferedReader):
String previous = null, line; // null means no previous line
while (line = yourReader.readLine()) {
// Do whatever with line
// If you need the previous line, use:
if (yourCondition) {
if (previous != null) {
// Do whatever with previous
} else {
// No previous line
}
}
previous = line;
}
If you need to keep track of more than one previous line, you may have to expand that into an array, but you will be keeping a huge amount in memory if your file is large--as much as if you'd read the entire file, once you get to the last line.
There is no simple way in Java or Android to read the previous line, only the next (as it is easier in file I/O to more forward than backward).
One alternative I can think of is to keep a line marker (starting at 0), and as you advance through the lines, increase it. Then, to go backwards, you have to read the file line by line again, until you get to that line minus one. If you need to go backwards, go to that new line minus one, and so on. It would be a heavy operation, most likely, but would suit your needs.
Edit: If nothing above will work, there is also a method to read in a file backwards, in which you may be able to use to find the previous line by iterating forward. Just an alternative idea, but definitely not an easy one to implement.
public class LoadFromAltLoc extends Activity {
//a handle to the application's resources
private Resources resources;
//a string to output the contents of the files to LogCat
private String output;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//get the application's resources
resources = getResources();
try
{
//Load the file from the raw folder - don't forget to OMIT the extension
output = LoadFile("from_raw_folder", true);
//output to LogCat
Log.i("test", output);
}
catch (IOException e)
{
//display an error toast message
Toast toast = Toast.makeText(this, "File: not found!", Toast.LENGTH_LONG);
toast.show();
}
try
{
//Load the file from assets folder - don't forget to INCLUDE the extension
output = LoadFile("from_assets_folder.pdf", false);
//output to LogCat
Log.i("test", output);
}
catch (IOException e)
{
//display an error toast message
Toast toast = Toast.makeText(this, "File: not found!", Toast.LENGTH_LONG);
toast.show();
}
}
//load file from apps res/raw folder or Assets folder
public String LoadFile(String fileName, boolean loadFromRawFolder) throws IOException
{
//Create a InputStream to read the file into
InputStream iS;
if (loadFromRawFolder)
{
//get the resource id from the file name
int rID = resources.getIdentifier("fortyonepost.com.lfas:raw/"+fileName, null, null);
//get the file as a stream
iS = resources.openRawResource(rID);
}
else
{
//get the file as a stream
iS = resources.getAssets().open(fileName);
}
//create a buffer that has the same size as the InputStream
byte[] buffer = new byte[iS.available()];
//read the text file as a stream, into the buffer
iS.read(buffer);
//create a output stream to write the buffer into
ByteArrayOutputStream oS = new ByteArrayOutputStream();
//write this buffer to the output stream
oS.write(buffer);
//Close the Input and Output streams
oS.close();
iS.close();
//return the output stream as a String
return oS.toString();
}
}
Related
I have searched a lot but it seems older answers are wrong as storage seems to have changed from data/data and permission WRITE_INTERNAL_MEMORY is no longer available. I am using Eclipse.
I have a multi-choice test and want to store the status of the answers a user has given:
N = not attepmpted, C = Correct last time, I = Incorrect last attempt
Therefore the file needs to be re-writeable - will be read as an array and then the array with new status will be over-written.
The code to write the file on first run is - you can see I've just changed it to write "N" now rather than lines of "N" as needed. There is also a single-line txt file to store the user id:
public void RunFirst(View view) throws IOException{
//need to initialise file as a list of N's:
count = 0;
StringBuilder sb = new StringBuilder();
while(count<4){
sb.append("N");
sb.append("\n");
count = count +1;
};
NsString = sb.toString();
String progfile = "userprogress.txt";
try {
FileOutputStream fos = new FileOutputStream(progfile);
fos = openFileOutput(progfile, Context.MODE_PRIVATE);
fos.write(NsString.getBytes());
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
// read userID
TextView usrID = (TextView) findViewById(R.id.editTextUserNameInput);
userID = usrID.getText().toString();
Toast.makeText(getApplicationContext(), "Welcome" + userID,
Toast.LENGTH_LONG).show();
//save userID
String usridfile = "userid.txt";
try{
FileOutputStream fosuserid = new FileOutputStream(usridfile);
fosuserid = openFileOutput(usridfile, Context.MODE_PRIVATE);
fosuserid.write(userID.getBytes());
fosuserid.close();
Toast.makeText(getApplicationContext(), "filesaved",
Toast.LENGTH_LONG).show();
} catch (Exception e) {
e.printStackTrace();
}
}
To read from the file:
private void readprogressfile(){
//#Override
try
{
Toast.makeText(getApplicationContext(), "b4 file",
Toast.LENGTH_LONG).show();
InputStream input = openFileInput("userprogress.txt");
InputStreamReader isr = new InputStreamReader(input);
BufferedReader buffrdr = new BufferedReader(isr);
userprog = new String [4];
int size = input.available();
byte[] buffer = new byte[size];
count = 0;
line = null;
while(count <4){
input.read(buffer);
line = new String(buffer);
userprog[count]= line;
Toast.makeText(getApplicationContext(), "status:" + count + userprog[count],
Toast.LENGTH_LONG).show();
};
input.close();
// byte buffer into a string
String text= new String(buffer);
//txtContent.setText(text);
Toast.makeText(getApplicationContext(), "after file",
Toast.LENGTH_LONG).show();
TextView showfile = (TextView)findViewById(R.id.textViewShowAns);
showfile.setText("Q status:"+ userprog[qno]);
}catch (IOException e) {
e.printStackTrace ();}
}
;
}
the fact that WRITE_INTERNAL_MEMORY permission is deprecated don't mean that you can't write to internal memory anymore. actually, it's the opposite - Google decided there is no need in any permission to write / create files in your private internal folder.
you can get path to your private application storage folder by the method getFilesDir()
this is the perfect place to write your private files, and made especially for that purpose.
as Google wrote in the documentation:
You don’t need any permissions to save files on the internal storage. Your application always has permission to read and write files in its internal storage directory.
source and more info on - http://developer.android.com/training/basics/data-storage/files.html
Got a brand new project using Google's new Android Studio IDE.
I'm trying to load a simple text file using an InputStreamReader. I'm getting a file not found exception. Now there isn't any assets/ folder. I tried to create one and add my file at many different spots (at the root of the project, at the root of the .java file, etc...) I've tried to move the file around but still get the file not found.
Now that never was a problem using Eclipse as there is an assets folder created by any template.
Does anyone know where should the assets go to or how to load them?
Here is the code used, it fails at .open():
InputStream iS = resources.getAssets().open("bla.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(iS));
I also tried this code in Eclipse, it works and the file contents get loaded. So there's probably a step needed in Android Studio.
Step 1:
Open in Name_Project-Name_Project.iml file.
See the line :
option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets"
Step 2:
Create a sub-folder "assets" in main folder.
Step 3:
Put file in this folder.
Step 4:
Load it. Done.
The correct answer didn't work for me exactly.
This works:
Go to Project view and then go to app/src/main and create new directory assets
to load the file:
InputStream is = getApplicationContext().getAssets().open("bla.txt");
or:
InputStream is = context.getAssets().open("bla.txt");
and then convert it to string at any way you want, examples here
detailed video of how to do it (not mine)
This code will work for you.It will fetch all data from file.
public class Quiz extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_quiz);
try {
PlayWithRawFiles();
} catch (IOException e) {
Toast.makeText(getApplicationContext(),
"Problems: " + e.getMessage(), Toast.LENGTH_LONG).show();
}
}// onCreate
public void PlayWithRawFiles() throws IOException {
String str="";
StringBuffer buf = new StringBuffer();
InputStream is = this.getResources().openRawResource(R.raw.ashraf);
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
if (is!=null) {
while ((str = reader.readLine()) != null) {
buf.append(str + "\n" );
}
}
is.close();
TextView tv=(TextView)findViewById(R.id.tv1);
tv.setText(buf.toString());
}//
}
//Compiled all the codes available in the internet, and this works perfectly fine for reading data from a textfile
//Compiled By: JimHanarose
ArrayList<String> data_base = new ArrayList<String>();
String text = "";
try {
InputStream is = getApplicationContext().getAssets().open("g.txt"); //save this .txt under src/main/assets/g.txt
int size = is.available();
StringBuffer buf = new StringBuffer();
byte [] buffer = new byte[size];
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
if (is!=null) {
while ((text = reader.readLine()) != null) {
data_base.add(text.toString()); //create your own arraylist variable to hold each line being read from g.txt
}
}
is.close();
} catch (IOException e) {
e.printStackTrace();
}
So this is a pretty embarrassing question, but i have a text file and java will read all of the words in it and add it to a array, i don't know where to put the text file, like what folder so the comp can go get it? could someone tell me. my code works in a regular java application, so it should work on android.
you can use
<your-context>.getAssets();
to return an AssetsManager object.
AssetsManager assets = context.getAssets();
You can then open an input stream with the open() method.
InputStream inputStream = assets.open("filename");
The InputStream object is a standard Java object from the IO package. You can decorate this stream with an object decorator you wish (Reader, BufferedReader, etc).
If you wish to move this file out of the APK (that is not inflated) to the phone you can just copy the bytes of the file from the input stream using an output stream. Note you will have to have permissions in your write directory (you can do this if your phone is rooted and you have created a shell interface to run native shell commands through JNI).
UPDATE
try {
InputStream inputStream = this.getAssets().open("test.txt");
BufferedReader buffer = new BufferedReader(new Reader(inputStream));
String line;
while((line = buffer.readLine()) != null) {
tots.add(line);
}
}
catch(IOException e) {
e.printStackTrace();
}
Haven't tested it, but I think this is what you want.
You can put the file to assets folder and use
InputStream stream = getAssets().open(filename);
to get the input stream
I created new raw folder in res folder and put chapter0.txt in here.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.induction);
wordss = new Vector<String>();
TextViewEx helloTxt = (TextViewEx) findViewById(R.id.test);
helloTxt.setText(readTxt());
}
private String readTxt() {
InputStream inputStream = getResources().openRawResource(R.raw.chapter0);
// getResources().openRawResource(R.raw.internals);
System.out.println(inputStream);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
int i;
try {
i = inputStream.read();
while (i != -1) {
byteArrayOutputStream.write(i);
i = inputStream.read();
}
inputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return byteArrayOutputStream.toString();
}
The following code works, but takes way too long (over a minute) to open a small file. The LogCat shows a lot of instances of "GC_FOR_MALLOC freed #### objects / ###### bytes in ##ms". Any suggestions?
File dirPath = new File(Environment.getExternalStorageDirectory(), "MyFolder");
String content = getFile("test.txt");
public String getFile(String file){
String content = "";
try {
File dirPathFile = new File(dirPath, file);
FileInputStream fis = new FileInputStream(dirPathFile);
int c;
while((c = fis.read()) != -1) {
content += (char)c;
}
fis.close();
} catch (Exception e) {
getLog("Error (" + e.toString() + ") with: " + file);
}
return content;
}
Update:
This is what it looks like now:
File dirPath = new File(Environment.getExternalStorageDirectory(), "MyFolder");
String content = getFile("test.txt");
public String getFile(String file){
String content = "";
File dirPathFile = new File(dirPath, file);
try {
StringBuilder text = new StringBuilder();
BufferedReader br = new BufferedReader(new FileReader(dirPathFile));
String line;
while ((line = br.readLine()) != null) {
text.append(line);
text.append('\n');
}
content = new String(text);
} catch (Exception e) {
getLog("Error (" + e.toString() + ") with: " + file);
}
return content;
}
Thank you all!!
Using += on a String is extremely inefficient - it will constantly allocate and deallocate memory, something you need to avoid!
If you need to constantly add characters, use a StringBuilder and give it a sufficiently big buffer up front.
However, it's even better to just read the entire file as a byte array and then create a string from that byte array. Use the String(byte[]) constructor.
content += (char)c;
Well, here's your problem. String concatenation is slow if you have to do it repeatedly. And you're reading the file one character at a time, which is also really slow.
You want to be using the read(byte[] buffer) method to read the file into a buffer efficiently. And then you can stringify the buffer if need be.
Rather than reading a single byte at a time, you should read multiple using read(byte[]).
Also, Strings are immutable, so every time you do String s = s + "a"; there is the possibility that you are creating a new String object. You can use StringBuilder instead to build up a larger string.
Schlemiel the painter strikes again!
try to read with buffer read(byte[] buff)
The reasons are:
You are creating too many String objects with content += (char)c; - use StringBuilder instead to append with read data, then in the end call toString() on the StringBuilder.
You don't use a byte[] (or char[], it depends on implementation) buffer to read from file. Usually 1KB buffer is optimal instead of reading one by one byte.
I see a lot of examples how to write String objects like that:
String FILENAME = "hello_file";
String string = "hello world!";
FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_PRIVATE);
fos.write(string.getBytes());
fos.close();
but not how to read them back from internal application file. Most of examples assume specific string length to calculate byte buffer but I do not know what the length will be. Is there an easy way to do so? My app will write up to 50-100 strings to the file
Writing strings this way doesn't put any sort of delimiters in the file. You don't know where one string ends and the next starts. That's why you must specify the length of the strings when reading them back.
You can use DataOutputStream.writeUTF() and DataInputStream.readUTF() instead as these methods put the length of the strings in the file and read back the right number of characters automatically.
In an Android Context you could do something like this:
try {
// Write 20 Strings
DataOutputStream out =
new DataOutputStream(openFileOutput(FILENAME, Context.MODE_PRIVATE));
for (int i=0; i<20; i++) {
out.writeUTF(Integer.toString(i));
}
out.close();
// Read them back
DataInputStream in = new DataInputStream(openFileInput(FILENAME));
try {
for (;;) {
Log.i("Data Input Sample", in.readUTF());
}
} catch (EOFException e) {
Log.i("Data Input Sample", "End of file reached");
}
in.close();
} catch (IOException e) {
Log.i("Data Input Sample", "I/O Error");
}