如何在Java中逐个字符读取输入?

57

我习惯于使用c风格的getchar(), 但在Java中好像没有类似的函数。我正在构建一个词法分析器,需要逐个字符地读取输入。

我知道可以使用扫描器(Scanner)扫描标记或行并通过标记逐个字符解析,但对于跨越多行的字符串来说似乎有些麻烦。在Java中是否有一种方法只获取输入缓冲区中的下一个字符呢,还是我需要继续使用Scanner类?

输入是一个文件,不是键盘。


在Java中,是否有可能使用System.in.read()读取一个字符而不等待回车键?:https://stackoverflow.com/questions/62122059/in-java-is-it-possible-to-system-in-read-reads-a-key-without-waiting-a-carriage - danilo
9个回答

65
使用Reader.read()方法。返回值为-1表示流结束;否则,将其强制转换为char
此代码从文件参数列表中读取字符数据。
public class CharacterHandler {
    //Java 7 source level
    public static void main(String[] args) throws IOException {
        // replace this with a known encoding if possible
        Charset encoding = Charset.defaultCharset();
        for (String filename : args) {
            File file = new File(filename);
            handleFile(file, encoding);
        }
    }

    private static void handleFile(File file, Charset encoding)
            throws IOException {
        try (InputStream in = new FileInputStream(file);
             Reader reader = new InputStreamReader(in, encoding);
             // buffer for efficiency
             Reader buffer = new BufferedReader(reader)) {
            handleCharacters(buffer);
        }
    }

    private static void handleCharacters(Reader reader)
            throws IOException {
        int r;
        while ((r = reader.read()) != -1) {
            char ch = (char) r;
            System.out.println("Do something with " + ch);
        }
    }
}

上述代码的问题在于它使用了系统默认的字符集。在可能的情况下,应优先选择已知编码(最好是Unicode编码)。有关更多信息,请参见Charset类。(如果您感到痛苦,可以阅读此字符编码指南)。

(您可能需要注意的一件事是补充的Unicode字符 - 需要两个字符值来存储的字符。请参见Character类以获取更多细节;这是一个边缘案例,可能不适用于作业。)


4
通常情况下,您需要打开一个FileInputStream并将其包装在InputStreamReader中,指定字符编码。(不幸的是,FileReader无法让您指定编码。) - Jon Skeet
我有一个问题,请问!如果我一次只读取一个字符,为什么我需要一个 BufferedReader? - kzidane
1
@KareemMesbah 缓冲读取可以提高性能,因为这意味着大多数对 read() 的调用来自 RAM 而不是操作系统/磁盘。代码可以在没有缓冲区的情况下工作,或者您可以使用 BufferedInputStream 或通过调用 read(char[]) 使用自己的缓冲区。 - McDowell
谢谢 - 这正是我一直在寻找的。 - thonnor
@McDowell 与使用读取器读取文件相比,性能如何? - beinghuman

21

参考其他人的建议,结合字符编码和缓冲输入的要求,以下是我认为比较完整的答案。

假设您有一个代表要读取的文件的File对象:

BufferedReader reader = new BufferedReader(
    new InputStreamReader(
        new FileInputStream(file),
        Charset.forName("UTF-8")));
int c;
while((c = reader.read()) != -1) {
  char character = (char) c;
  // Do something with your character
}

8

另一种选择是不逐个字符地读取内容,而是将整个文件读入内存。如果您需要多次查看字符,则这很有用。一种简单的方法是:

  /** Read the contents of a file into a string buffer      */
    public static void readFile(File file, StringBuffer buf)
        throws IOException
    {
    FileReader fr = null;
    try {
      fr = new FileReader(file);
      BufferedReader br = new BufferedReader(fr);
      char[] cbuf = new char[(int) file.length()];
      br.read(cbuf);  
      buf.append(cbuf);
      br.close();
    }
    finally {
      if (fr != null) {
        fr.close();
      }
    }
}

char[] 也可以用于稍后在文件中进行搜索。StringBuffer 仅用于将字符数组附加到 StringBuffer 中,并将其传回执行调用点。我想当 buf 进入该方法时,它应该是空的。 - Doug Hauf
这是一个示例方法,用于演示概念。要实际使用该技术,我建议使用像Guava这样的库。 - David
考虑到您已经在使用 BufferedReader,这种方法可能比设置标记并重置读取器缓冲区更慢。在使用此方法之前,最好获取性能指标。 - Txangel

7

将您的输入流包装在缓冲读取器中,然后使用read方法逐个字节读取,直到流的末尾。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Reader {

    public static void main(String[] args) throws IOException {

        BufferedReader buffer = new BufferedReader(
                 new InputStreamReader(System.in));
        int c = 0;
        while((c = buffer.read()) != -1) {
            char character = (char) c;          
            System.out.println(character);          
        }       
    }   
}

2

如果我是你,我会使用扫描仪并使用“.nextByte()”方法。你可以将其转换为字符类型,就可以了。


1
如果您使用BufferedReader,则有几个选项可供选择。这个缓冲读取器比Reader更快,因此您可以将其包装起来。
BufferedReader reader = new BufferedReader(new FileReader(path));
reader.read(char[] buffer);

这个函数将一行读入到字符数组中。你有类似的选项。请查看文档。


1

使用BufferedReader将读者包裹起来,它维护一个缓冲区,从而实现更快的整体读取。然后可以使用read()方法读取单个字符(需要进行类型转换)。还可以使用readLine()方法获取整行文本,然后将其拆分为单个字符。如果需要,BufferedReader还支持标记和返回,因此可以多次读取一行。

一般来说,您应该在实际使用的任何流之上使用BufferedReader或BufferedInputStream,因为它们维护的缓冲区将使多次读取变得更快。


0

在Java 5中添加了新功能,即Scanner方法,它提供了在Java中逐个字符读取输入的机会。

例如; 要使用Scanner方法,请导入java.util.Scanner; 然后在主方法中:定义

Scanner myScanner = new Scanner(System.in); //用于读取字符

char anything=myScanner.findInLine(".").charAt(0);

您可以存储任何单个字符,如果要读取更多字符,请声明更多对象,如anything1、anything2... 有关您的答案的更多示例,请在手头查看(复制/粘贴)

     import java.util.Scanner;
     class ReverseWord  {

    public static void main(String args[]){
    Scanner myScanner=new Scanner(System.in);
    char c1,c2,c3,c4;

    c1 = myScanner.findInLine(".").charAt(0);
        c2 = myScanner.findInLine(".").charAt(0);
    c3 = myScanner.findInLine(".").charAt(0);
    c4 = myScanner.findInLine(".").charAt(0);

    System.out.print(c4);
    System.out.print(c3);
    System.out.print(c2);
    System.out.print(c1);
    System.out.println();

   }
  }

-1

这将从文件中每行打印1个字符。

    try {

        FileInputStream inputStream = new FileInputStream(theFile);
        while (inputStream.available() > 0) {
            inputData = inputStream.read();
            System.out.println((char) inputData);

        }
        inputStream.close();
    } catch (IOException ioe) {
        System.out.println("Trouble reading from the file: " + ioe.getMessage());
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接