3

Problem description: For example we have a text file file.txt with following content:

Hello(LS)
what(LS)
are(LS)
<empty_line>(LS)
you(LS)
doing(LS)
<empty_line>(LS)
<empty_line>(LS)
now(LS)
<empty_line>(LS)
<empty_line>(LS)
<empty_line>(EOF)

(LS - line separator, EOF - end of file)

If I understood the idea of text file, the file is looking something like that. Now, I want to fill up for example TextArea (from JavaFX) with this text. I can use the following code surrounded with try-catch (close() method would be in finnaly block):

TextArea textArea = new TextArea();
BufferedReader br = new BufferedReader(new FileReader(new File("file.txt")));
String line;
while ((line = br.readLine()) != null) {
       textArea.appendText(line);
       textArea.appendText("\n");
}

That code has one big problem - everytime there is one more line in the targeted TextArea when text file does't ending with empty_line. When file that empty_line contains before EOF, so loading into TextArea is working right. I tried a lot of way how to do it, but everytime there was some problems.

Why I chose BufferedReader? I chose BufferedReader because I just want some reader that can reads source text file line by line. I want that reader, because of file separator and OS independence.

What I expect from it? I am awaiting that I read file line by line and when I am reading I set up custom separators (independently for source file line separators) - for example LF ("\n"). And I am awaiting exactly the same content with possible different line separators! When I am writing to some file (where I am using BufferedReader too (for intern reading text from TextArea and set up line separators depended on user OS (System.getProperty("line.separator"))) there is a similar behavioral.

What I tried? I tried a lot of ways how to do it. I also tried use method read() in class BufferedReader and compare it's return value with -1, which is means EOF. BUT, there is one problem, I want to use custom file separators and I don't have all time to write some parsers.

My question: How can I write to TextArea (or any String) the file content with customed line separators without any extra or less line?

*As you can see, my english isn't very good, so I hope you understand me, thanks.

Nik Novák
  • 569
  • 1
  • 9
  • 24

3 Answers3

4

You are adding the newline yourself.

while ((line = br.readLine()) != null) {
       textArea.appendText(line);
       textArea.appendText("\n");
}

The second line in while-loop adds the next line. Everytime that statement gets executed, the cursor will go to next line. So when the file cursor reads the last line from file, it appends it to the textArea and the then appends it with a new line character. Thats why you get extra blank line at the end.

if((line = br.readLine()) != null)
     textArea.appendText(line);
while ((line = br.readLine()) != null) {
       textArea.appendText("\n");
       textArea.appendText(line);
}
alwaysAStudent
  • 2,110
  • 4
  • 24
  • 47
  • Hello, I tried that, because I think that is the right solution, but you can try it yourself. Now I've tried it again and I get the same result - when I load file with 3 last lines empty, reader loads only 2 empty lines. – Nik Novák Jun 09 '15 at 23:44
  • @NikNovák How can you tell? – user207421 Jun 09 '15 at 23:48
  • I am trying it actually. I really don't know why, but when I have a file which have at least one last line empty, so one line disappear when I am trying to save in some String or TextArea ... very strange behavioral. Have you got this problem too? – Nik Novák Jun 09 '15 at 23:54
  • `StringBuilder output = new StringBuilder();` `BufferedReader br = new BufferedReader(new FileReader(new File("RandomFile.txt")));` `String line;` `if((line = br.readLine()) != null)` `output.append(line);` ` while ((line = br.readLine()) != null) {` `output.append("\n");` `output.append(line);` `}` `System.out.println(output);` This is code I tried. It gave me what I expect is the result you want. After printing 'now' it printed three empty lines. – alwaysAStudent Jun 10 '15 at 16:50
  • Sorry, but it wasn't working for me, you can just try it instead of printing write to some string or directly to TextArea. – Nik Novák Jun 10 '15 at 22:06
3

As it turned out in the discussion, the problem is that with readLine() the following files will behave the same way:

File A: "Hello\n"
File B: "Hello"

1st readLine() --> "Hello"
2nd readLine() --> null

==> we cannot know if there was a '\n' after the last line or not

Solution: use other methods to read the characters, two of them explained here:

One character at a time:

String readFile(String file) throws IOException {
    FileReader fr = new FileReader(new File(file));
    String s = "";
    int c;
    while ((c = fr.read()) >= 0) {
        s += (c == '\n' ? myCharOrString : (char)c;
    }
    return s;
}

Buffered:

String readFile(String file, int bufferSize) throws IOException {
    FileReader fr = new FileReader(new File(file));
    char[] buffer = new char[bufferSize];
    int charsRead;
    String s = "";
    while ((charsRead = fr.read(buffer)) > 0) {
        s += new String(buffer, 0, charsRead);
    }
    return s.replaceAll("\\r\\n?", "\n");
}
maraca
  • 8,468
  • 3
  • 23
  • 45
  • Sorry, but that isn't working too. When I am loading file with differents number of last empty lines it works. But when I use the text that isn't finished with any empty line, that empty line is in resultant String added. I just want copy of file content with possibility chose own line separators. – Nik Novák Jun 10 '15 at 00:14
  • @NikNovák are you sure, in that case it should remove the new line character at the end, because in substring the end index is exclusive. Are you doing it exactly like this or are you appending to the text area? – maraca Jun 10 '15 at 00:17
  • Oh, sorry, my bad; I just edit your code to `!s.endsWith("\n")`, because there was a problem with your original code: when I have last line empty in a file, this line wasn't in the resultant String. Otherwise it is functional. – Nik Novák Jun 10 '15 at 00:25
  • 2
    @NikNovák I see the problem, the answer basically is you have no way of detecting this when using readline `"Hello\nEOF"` and `"HelloEOF"` will behave the same way. – maraca Jun 10 '15 at 00:36
  • No, I can't do it exactly like you. The method in I have that piece of code is throwed through IOException. I can't use static String nor block. But rest of that I have exactly the same and problem which I describes above persists. – Nik Novák Jun 10 '15 at 00:37
  • @NikNovák yes, there are solutions, read() single characters directly from the FileReader or what I recommend with `public int read(char[] cbuf, int offset, int length)`. Can adjust tomorrow, basically you read just a bunch of character in an array of length 1024 or something and keep increasing the offset and write the amount of characters which is returned. – maraca Jun 10 '15 at 00:47
  • @macara As I wrote above, my goal was set custom line separators instead default line separators and correct load file content. Unfortunately, your answer is including only the second requirement. I already know how to accomplish all requirements (my post where I am using regex expression in this question). I would be happy to accept your answer, but it must match question solution. Please, can you edit your answer whose version will solving the problem? – Nik Novák Jun 10 '15 at 20:46
  • 1
    @NikNovák Added it to both now, it is \\n because replaceAll uses regex. – maraca Jun 10 '15 at 21:40
  • @macara It worked for me anyway. Sorry, but I do not know very well regex... Okey, anyway I am happy for that we get to the right solution. Thank you a lot! – Nik Novák Jun 10 '15 at 21:54
2

Okey, I did it! I don't know how much is that effective or if I can use different Reader than BufferedReader, but this is working for me:

TextArea textArea = new TextArea();
String someString;
bufferedReader = new BufferedReader(new FileReader(file));

StringBuilder codeTextArea = new StringBuilder();
int character;
while ((character = bufferedReader.read()) != -1)
       codeTextArea.append((char) character);
//for TextArea you can use just toString and it set up separators to LF
this.textArea.setText(codeTextArea.toString());
//for String where you want line separator LF you can use for example following regex expression
someString = codeTextArea.toString().replaceAll("\r\n?","\n");

As you can see in the second case I am using regex expression for detecting and replace line separators. That code can read text files with separators LF, CR, CRLF and fill up it correctly (includes last empty line) in TextArea or String with the lowest memory requirement, because of one character ("\n") for line separate and also is corresponding with TextArea default line separator (it is using the same - LF).

That code probably isn't very simplify, but it works. So I am asking you if you know how to simplify it for best possible hardware performance.

Nik Novák
  • 569
  • 1
  • 9
  • 24
  • Ok, final note, just read about it, \n also represents the character and not the line break on different systems http://stackoverflow.com/questions/20056306/match-linebreaks-n-or-r-n So, for your regex to be 100% correct you would have to write `replaceAll("\\r\\n?","\n")` although the other way might work too. – maraca Jun 10 '15 at 22:26