So I'm trying to read data from a .json file in my Android project:
val file = context.assets.open("myfile.json").bufferedReader().readText()
This works fine and successfully prints out my .json file as one String.
However I want to know what exactly is bufferedReader() and why .readText() can't be called directly on the opened .json file.
PS: readText() returns a String. However:
val json2: JSONObject = JSONObject("mystring") returns:
Caused by: org.json.JSONException: Value mystring of type java.lang.String cannot be converted to JSONObject
How does this make sense?
The readText function is defined as an extension on Reader:
public fun Reader.readText(): String {
val buffer = StringWriter()
copyTo(buffer)
return buffer.toString()
}
An InputStream isn't a Reader, so you have to transform it into some Reader:
public inline fun InputStream.reader(charset: Charset = Charsets.UTF_8): InputStreamReader =
InputStreamReader(this, charset)
You can use the reader as a buffered reader with the alternative bufferedReader function:
public inline fun InputStream.bufferedReader(charset: Charset = Charsets.UTF_8): BufferedReader =
reader(charset).buffered()
Reader and also BufferedReader are part of the Java standard library and the buffered version is described like this:
Reads text from a character-input stream, buffering characters so as to
provide for the efficient reading of characters, arrays, and lines.
In general, each read request made of a Reader causes a corresponding read request to be made of the underlying character or byte stream. It is
therefore advisable to wrap a BufferedReader around any Reader whose read()
operations may be costly, such as FileReaders and InputStreamReaders...
It basically wraps a Reader and adds support for reading single lines etc.
val file = context.assets.open("myfile.json").bufferedReader().readText()
Here is the code that do the same thing as above line.
val inputStream = context.assets.open("myfile.json")
val reader = inputStream.bufferedReader()
val file = reader.readText()
Assume here is the content of myfile.json
{
"os": "Android",
"version": "KitKat",
"codeName": 4.4
}
Let's go step by step
Step 1: The first line
val inputStream = context.assets.open("myfile.json")
This will be return an InputStream object, which reads one byte or number of bytes from the json file. If you print the json file content in byte format on the screen, it will really hard for us (as programmers) to read.
Step 2: The second line
val reader = inputStream.bufferedReader()
This will create a BufferedReader object, which read a character or a number of characters from the json file, but they have another useful method named readLine(), this method reads a line of text. A line is considered to be terminated by any one of a line feed ('\n'), a carriage return ('\r'), or a carriage return followed immediately by a linefeed.
Let's modify the current code.
val inputStream = context.assets.open("myfile.json")
val reader = inputStream.bufferedReader()
// Read line by line from reader until reach the end.
var line = reader.readLine()
while(line != null) {
Log.i("TAG", line)
line = reader.readLine()
}
Output:
I/TAG: {
I/TAG: "os": "Android",
I/TAG: "version": "KitKat",
I/TAG: "codeName": 4.4
I/TAG: }
As we can see, they print 5 lines from the json file. But in some cases, we want to print all the json file as a String, that why we move to next step.
Step 3: The third line
val file = reader.readText()
This will reads the buffer reader completely as a String. You can write your own to do the same like.
val inputStream = context.assets.open("myfile.json")
val reader = inputStream.bufferedReader()
val sb = StringBuffer()
var line = reader.readLine()
while(line != null) {
Log.i("TAG", line)
sb.append(line).append("\n")
line = reader.readLine()
}
val file = sb.toString()
Log.i("TAG", file)
Output:
I/TAG: {
"os": "Android",
"version": "KitKat",
"codeName": 4.4
}
This output is the same as reader.readText().
Conclusion: BufferReader wraps an InputStream (or sub-classes of InputStream) inside them, then provide methods to read character-by-character instead of byte-by-byte in InputStream. In addition, they provide readLine() method, buffer data.
InputStream (byte-by-byte) -> Reader (character-by-character)
InputStream (byte-by-byte) -> BufferReader (character-by-character,
read line, buffer data).
Related
I am developing an Android Library that contains some files with data. I want to package this library into an AAR file and distribute it. I'm having trouble opening the resource files within this library.
Here is a scenario that is analogous to mine:
In the Library there is a file labels.txt which is stored in res/raw.
I have a class LabelReader that sits inside the library. It has a function read() that will return the string contents of labels.txt.
package com.library.mine
class LabelReader {
fun read():string {
val resourceID = Resources.getSystem().getIdentifier("labels.txt", "raw", "com.library.mine") //returns 0
//Exception here
val br = BufferedReader(InputStreamReader(Resources.getSystem().openRawResource(identifier)))
...
}
}
I try to use the function in the app and that triggers the exception:
package com.app.mine
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val labelReader = LabelReader()
val labels = LabelReader.read() //problem
}
}
From what I have read it's not possible to include the file in the assets directory of an android library.
Most questions I have googled seem to make use of the Activity's Context, but that's not available to me here.
What is the correct way to handle this?
In my AAR libraries, I use to include and use raw resources. To use them, in the library or in the client application you need to do the same things:
Use the openRawResource method of Resources object (from Context).
Use raw resource name (letter/number and _) without the file extension.
A simple method to read raw resource content as text:
public static String readRawTextFile(Context context, int rawResourceId) throws IOException {
StringBuilder buffer = new StringBuilder();
InputStream inputStream = context.getResources().openRawResource(rawResourceId);
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream));
String read = in.readLine();
while (read != null) {
buffer.append(read + "\n");
read = in.readLine();
}
inputStream.close();
buffer.deleteCharAt(buffer.length() - 1);
return buffer.toString();
}
I hope this helps.
I've been researching about how diablo 2 dynamically generates loot, and I thought it'd be fun to create a fun app that will randomly generate items using this system.
I currently have code which I believe should read the entire txt file, but it's not parsed.
It looks like:
private void itemGenerator() {
int ch;
StringBuffer strContent = new StringBuffer("");
InputStream fs = getResources().openRawResource(R.raw.treasureclass);
// read file until end and put into strContent
try {
while((ch = fs.read()) != -1){
strContent.append((char)ch);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
An example in the text file would look something like:
Treasure Class Item1 Item2 Item3
tc:armo3 Quilted_Armor Buckler Leather_Armor
tc:armo60a Embossed_Plate Sun_Spirit Fury_Visor
tc:armo60b Sacred_Rondache Mage_Plate Diadem
So what I'm thinking right now is putting each row into an array with StringTokenizer delimited by \n to get each row. Then somehow do it again with tab-delimited for each item in the array and put it into a 2D array?
I haven't coded it yet because I think there's a better way to implement this that I haven't been able to find, and was hoping for some helpful input on the matter.
For anyone actually interested in knowing how the item generation works, their wiki page, http://diablo2.diablowiki.net/Item_Generation_Tutorial, goes very in-depth!
I think you are facing problem in distinguishing between each lines that are read-out from file. In order to read the file line-by-line you should change your code as below:
InputStream fs = getResources().openRawResource(R.raw.treasureclass);
BufferedReader br = new BufferedReader(new InputStreamReader(fs));
String line = null;
while((line = br.readLine()) != null){
Log.i("line", line);
//split the content of 'line' and save them in your desired way
}
InputStream inputStream = getResources().openRawResource(idtext);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
String myText = "";
int in;
try {
in = inputStream.read();
while (in != -1) {
byteArrayOutputStream.write(in);
in = inputStream.read();
}
inputStream.close();
myText = byteArrayOutputStream.toString();
} catch (IOException e) {
e.printStackTrace();
}
myTextView.setText(myText); `
My code is used to display long text file in raw res. I don't know why, but some of text file display wrong about next line, any help?
It would be good if you can share some sample display vs your expected output.
From the initial guess, it could be because of couple of reasons:
The encoding used in the text file. So, if you have written the text in ASCII and displaying it while using UTF-8 strings, it would mess up few things. This should be consistent.
Could be a good case of how line feeds are encoding in the file, like \r\n or just \n.
You can also try encapsulating your InputStream to FileReader and line reading streams which are more specialized in directly reading strings, rather than converting it.
You can probably use a library like Apache Commons IO to manage all the stuffs for you.
I would like to store some strings in a simple .txt file and then read them, but when I want to encode them using Base64 it doesn't work anymore: it writes well but the reading doesn't work. ^^
The write method:
private void write() throws IOException {
String fileName = "/mnt/sdcard/test.txt";
File myFile = new File(fileName);
BufferedWriter bW = new BufferedWriter(new FileWriter(myFile, true));
// Write the string to the file
String test = "http://google.fr";
test = Base64.encodeToString(test.getBytes(), Base64.DEFAULT);
bW.write("here it comes");
bW.write(";");
bW.write(test);
bW.write(";");
bW.write("done");
bW.write("\r\n");
// save and close
bW.flush();
bW.close();
}
The read method :
private void read() throws IOException {
String fileName = "/mnt/sdcard/test.txt";
File myFile = new File(fileName);
FileInputStream fIn = new FileInputStream(myFile);
BufferedReader inBuff = new BufferedReader(new InputStreamReader(fIn));
String line = inBuff.readLine();
int i = 0;
ArrayList<List<String>> matrice_full = new ArrayList<List<String>>();
while (line != null) {
matrice_full.add(new ArrayList<String>());
String[] tokens = line.split(";");
String decode = tokens[1];
decode = new String(Base64.decode(decode, Base64.DEFAULT));
matrice_full.get(i).add(tokens[0]);
matrice_full.get(i).add(tokens[1]);
matrice_full.get(i).add(tokens[2]);
line = inBuff.readLine();
i++;
}
inBuff.close();
}
Any ideas why?
You have a couple of errors in your code.
First a couple of notes on your code:
When posting here, attaching a SSCCE helps others to debug your code. This is not a SSCEE because it doesn't compile. It lacks several defined variables, so one must guess what you really mean. Also you have pasted close-comment token in your code: */ but there is no one start-comment token.
Catching and just suppressing exceptions (like in catch-block in read method) is really bad idea unless you really know what you're doing. What it does most of the time is hide the potential problems from you. At least write the stacktrace of an exception is a catch block.
Why don't you just debug it, check what exactly outputs to the destination file? You should learn how to do that because that will speed up your development process, especially for larger projects with hard-to-catch problems.
Back to the solution:
Run the program. It throws an exception:
02-01 17:18:58.171: E/AndroidRuntime(24417): Caused by: java.lang.ArrayIndexOutOfBoundsException
caused by line here:
matrice_full.get(i).add(tokens[2]);
inspecting the variable tokens reveals that it has 2 elements, not 3.
So lets open the file generated by the write method. Doing that shows this output:
here it comes;aHR0cDovL2dvb2dsZS5mcg==
;done
here it comes;aHR0cDovL2dvb2dsZS5mcg==
;done
here it comes;aHR0cDovL2dvb2dsZS5mcg==
;done
Note line breaking here. This is because the Base64.encodeToString() appends additional newline at the end of the encoded string. To generate a one single line, without extra newlines, add Base64.NO_WRAP as the second parameter like this:
test = Base64.encodeToString(test.getBytes(), Base64.NO_WRAP);
Note here, you must delete file that was created earlier as it has improper line breaking.
Run the code again. It now creates a file with the proper contents:
here it comes;aHR0cDovL2dvb2dsZS5mcg==;done
here it comes;aHR0cDovL2dvb2dsZS5mcg==;done
Printing the output of matrice_full now gives:
[
[here it comes, aHR0cDovL2dvb2dsZS5mcg==, done],
[here it comes, aHR0cDovL2dvb2dsZS5mcg==, done]
]
Note that you're not doing anything with the value in decode variable in your code, hence the second element is the Base64 representation of that value which is read from the file.
I have a text file which contains data I need to preload into a SQLite database. I saved in in res/raw.
I read the whole file using readTxtFromRaw(), then I use the StringTokenizer class to process the file line by line.
However the String returned by readTxtFromRaw does not show foreign characters that are in the file. I need these as some of the text is Spanish or French. Am I missing something?
Code:
String fileCont = new String(readTxtFromRaw(R.raw.wordstext));
StringTokenizer myToken = new StringTokenizer(fileCont , "\t\n\r\f");
The readTxtFromRaw method is:
private String readTxtFromRaw(Integer rawResource) throws IOException
{
InputStream inputStream = mCtx.getResources().openRawResource(rawResource);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
int i = inputStream.read();
while (i != -1)
{
byteArrayOutputStream.write(i);
i = inputStream.read();
}
inputStream.close();
return byteArrayOutputStream.toString();
}
The file was created using Eclipse, and all characters appear fine in Eclipse.
Could this have something to do with Eclipse itself? I set a breakpoint and checked out myToken in the Watch window. I tried to manually replace the weird character for the correct one (for example í, or é), and it would not let me.
Have you checked the several encodings?
what's the encoding of your source file?
what's the encoding of your output stream?
the byteArrayOutputStream.toString() converts according to the platform's default character encoding. So I guess it will strip the foreign characters or convert them in a way that they are not displayed in your output.
Have you already tried to use byteArrayOutputStream.toString(String enc)? Try "UTF-8" or "iso-8859-1" or "UTF-16" for the encoding.