java从文件中读取文本后,打印将在开头"null"

use*_*316 3 java string null

我从.txt文件中读到了这个:

<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
   <allow-access-from domain="*" to-ports="*" />
</cross-domain-policy>
Run Code Online (Sandbox Code Playgroud)

但阅读之后再输出我得到这个:

null<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
   <allow-access-from domain="*" to-ports="*" />
</cross-domain-policy>
Run Code Online (Sandbox Code Playgroud)

在开头为null,这是方法:

public class Filer {

    private static String str;
    public static String read(String file) {

        BufferedReader br = null;
        try {

            String sCurrentLine;

            br = new BufferedReader(new FileReader(file));

            while ((sCurrentLine = br.readLine()) != null) {

                str += sCurrentLine;
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (br != null)br.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }

        return str;

    }
}
Run Code Online (Sandbox Code Playgroud)

返回的字符串被输出 System.out.println

我试过通过str.indexOf("null");或检查字符串,str.indexOf("\0");但我得到-1

有想法该怎么解决这个吗?

Roh*_*ain 5

null引用与字符串连接时,它将转换为"null"字符串,然后执行连接.

这在JLS - 第5.1.11节中规定:

如果引用为null,则将其转换为字符串"null"(四个ASCII字符n,u,l,l).

现在,当您声明一个实例或静态引用字段时,如果没有初始化为任何值,它将被初始化为默认值 - null如您的声明中所示:

private static String str;  // This is `null`.
Run Code Online (Sandbox Code Playgroud)

然后是循环的第一次迭代:

str += sCurrentLine;
Run Code Online (Sandbox Code Playgroud)

相当于:

str = null + sCurrentLine;  // Hence the result.
Run Code Online (Sandbox Code Playgroud)

至于你的str.indexOf()查询,我不知道你正在测试的指标,因为它应该返回的指标null0,只要你使用的方法中的循环后,如在下面的代码片段:

String str = null;
String str2 = str + "someString";
System.out.println("Index of null in : " + str2 + " : " + str2.indexOf("null"));
Run Code Online (Sandbox Code Playgroud)

这将输出给你:

Index of null in : nullsomeString : 0
Run Code Online (Sandbox Code Playgroud)

此外,在这样的循环中执行字符串连接时,您无法控制,当它结束时,甚至一般情况下,您应该使用StringBuilder实例,以避免创建中间字符串实例:

StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(sCurrentLine);
Run Code Online (Sandbox Code Playgroud)


Jon*_*eet 5

考虑循环的第一次迭代.的值str是零,而价值sCurrentLine"<?xml version="1.0"?>".

在字符串连接中,空引用将转换为"null"字符串.例如:

String x = null;
String y = "a";
String z = x + y; // "nulla"
Run Code Online (Sandbox Code Playgroud)

所以在第一次迭代时,执行时:

str += sCurrentLine;
Run Code Online (Sandbox Code Playgroud)

价值str将是"null<?xml version="1.0"?>"

可以初始化str""...但我不会.

无论如何它都不应该是一个静态变量 - 即使再次调用该方法,你真的想保持旧值不变吗?那将是非常奇怪的.它应该是一个局部变量.

此外,你不应该在这样的循环中使用字符串连接 - 它在性能方面很糟糕,因为它需要不断复制旧数据.请StringBuilder改用.

我也不会捕捉这样的异常 - 你真的希望调用者不知道出了什么问题吗?只是声明你的方法可以抛出IOException.

另外,我不会使用FileReader自己 - 它无法控制用于读取文件的编码 - 它将始终使用平台默认编码,这可能是不合适的.我会用一个FileInputStream包裹的InputStreamReader.然后,您可以使用try-with-resourcesJava 7中的语句FileInputStream自动关闭.

因此,通过所有这些更改,您的代码将变为如下所示:

public static String read(String file) throws IOException {
    StringBuilder builder = new StringBuilder();
    try (InputStream input = new FileInputStream(file)) {
        BufferedReader reader = new BufferedReader(
            new InputStreamReader(input, "UTF-8"));
        String line;
        while ((line = reader.readLine()) != null) {
            builder.append(line);
        }
    }
    return builder.toString();
}
Run Code Online (Sandbox Code Playgroud)

请注意,如果要保留行数(但不关心行分隔符),这会将多行文件转换为单行文本 - builder.append("\n")在循环中使用或类似的东西.如果要保留确切的行结尾,请不要使用readLine- 只需将字符读入缓冲区即可.

另外,考虑使用像Guava这样的第三方库来代替所有这些 - 例如,您可以使用Files.toString().