Fileinputstream class read() method not finding end of file

Asked

Viewed 50 times

2

This program aims to copy the contents of the text file origin.txt and paste it into the file destiny.txt, each space read in the source file, a hyphen must be pasted in the target file.

The noose while serves to read characters from the source file until it reaches its end, that is, until the method read() class FileInputStream return -1.

However, the loop is not only reading the source file to its end, because in addition to reading all the characters in the source file and pasting them into the target file, it is also reading and pasting raw bytes (which in the Vscode editor, appear only as question marks). Here are some images that illustrate the problem, and the program:

Source file - https://ibb.co/mqV5R8B
Destination file (Where the source content will be pasted) - https://ibb.co/mTJpZWq

import java.io.*;

public class Demo {
  public static void main(String[] args) throws FileNotFoundException {
    int r = 0;

    FileInputStream fin = new FileInputStream("origin.txt");
    FileOutputStream fout = new FileOutputStream("destiny.txt");

    try {
      while (r != -1) {
        r = (char) fin.read();

        if (r == ' ')
          fout.write('-');
        else
          fout.write(r);
      }
    }

    catch (IOException exc) {
      System.out.println("Error while copying the files");
    }

    try {
      fin.close();
      fout.close();
    }

    catch (IOException exc) {
      System.out.println("Error while closing the files");
    }
  }
}

1 answer

2

The problem is on this line:

r = (char) fin.read();

But before you understand, consider the code below:

int x = (char) -1;
System.out.println(x);

The code above prints 65535. This happens basically because the literal -1 is a int (that in Java takes 32 bits), and in doing cast for char (which only has 16 bits), the extra bits are "lost".

Only that int is a guy Signed (with signal, allows representing positive and negative numbers) and char is unsigned (no signal, only represents positive numbers). That is, although bytes are the same, the way they are interpreted can change. In a very concise way, the -1 is represented with all bits equal to 1 (according to complement rule 2). In doing cast for char, the extra bits are "lost", but there are still 16 bits left equal to 1. But as char is unsigned, this is interpreted as the number 65535. And this is the value that r receives in your code.

That is, your while never ends, because r will never be equal to -1.

To solve, just don’t do the cast:

while ((r = fin.read()) != -1) {
    if (r == ' ')
        fout.write('-');
    else fout.write(r);
}

After all, if you look at the documentation, you will see that read returns a int and write gets a int as parameter. There is no need to do the cast, not least because int and char are "interchangeable":

int x = ' ';
System.out.println(x); // 32 - valor do espaço na tabela ASCII
x = 32;
// imprime como char
System.out.println("[" + (char) x + "]"); // [ ]
System.out.println(x == ' '); // true

Another detail is that the way you are closing the files is wrong. To ensure that they are closed, place calls from close in a block finally:

FileInputStream fin = null;
FileOutputStream fout = null;
try {
    fin = new FileInputStream("origin.txt");
    fout = new FileOutputStream("destiny.txt");
    int r;
    while ((r = fin.read()) != -1) {
        if (r == ' ')
            fout.write('-');
        else
            fout.write(r);
    }
} catch (IOException exc) {
    System.out.println("Error while copying the files");
} finally {
    try {
        if (fin != null)
            fin.close();
    } catch (IOException exc) {
        System.out.println("Error while closing the files");
    }
    try {
        if (fout != null)
            fout.close();
    } catch (IOException exc) {
        System.out.println("Error while closing the files");
    }
}

So you ensure that the files will be closed at the end.

Or, from Java 7, prefer to use a block Try-with-Resources, which already closes everything automatically, without the need for a block finally:

try (FileInputStream fin = new FileInputStream("origin.txt");
     FileOutputStream fout = new FileOutputStream("destiny.txt")) {
    int r;
    while ((r = fin.read()) != -1) {
        if (r == ' ')
            fout.write('-');
        else
            fout.write(r);
    }
} catch (IOException exc) {
    System.out.println("Error while copying the files");
}

Another option is to use a BufferedReader to read multiple characters at once, instead of reading one by one:

char[] buffer = new char[1024];
try (Reader in = new BufferedReader(new FileReader("origin.txt"));
     Writer out = new BufferedWriter(new FileWriter("destiny.txt"))) {
    int lidos;
    while ((lidos = in.read(buffer)) != -1) {
        for (int i = 0; i < lidos; i++) {
            if (buffer[i] == ' ')
                buffer[i] = '-';
        }
        out.write(buffer, 0, lidos);
    }
} catch (IOException exc) {
    System.out.println("Error while copying the files");
}

Or, if the file is not too large, you can also upload all the content into one String, replace and write it in the other file:

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;


Path origin = Paths.get("origin.txt");
Charset charset = StandardCharsets.UTF_8; // escolha o encoding no qual o arquivo está
String content = new String(Files.readAllBytes(origin), charset);
Files.write(Paths.get("destiny.txt"), content.replaceAll(" ", "-").getBytes(charset));

For the above code, I used the bundle java.nio.file

Browser other questions tagged

You are not signed in. Login or sign up in order to post.