While循环未读取最后一项。

5

我要读取一个多行字符串,然后对其进行分割并打印出来。以下是字符串:

1T1b5T!1T2b1T1b2T!1T1b1T2b2T!1T3b1T1b1T!3T3b1T!1T3b1T1b1T!5T1*1T

11X21b1X
4X1b1X

当我使用 ! 分割字符串时,我得到了以下结果(不包括最后一行的字符串):
1T1b5T
1T1b5T1T2b1T1b2T
1T2b1T1b2T1T1b1T2b2T
1T1b1T2b2T1T3b1T1b1T
1T3b1T1b1T3T3b1T
3T3b1T1T3b1T1b1T
1T3b1T1b1T5T1*1T
5T1*1T11X21b1X
11X21b1X

以下是我的代码:

import java.io.BufferedInputStream;
import java.util.Scanner;

public class Main {

    public static void main(String args[]) {
        Scanner stdin = new Scanner(new BufferedInputStream(System.in));
        while (stdin.hasNext()) {
            for (String line : stdin.next().split("!")) {
                System.out.println(line);

                for (int i = 0; i < line.length(); i++) {
                    System.out.print(line.charAt(i));
                }
            }
        }
    }

}

我在哪里犯了错误,为什么最后一行没有读取?当我正确地读取所有行之后,如果我遇到数字,我应该打印下一个字符,然后再次打印刚刚读取的数字n次,但这是很长的路要走,首先我需要帮助。谢谢。

更新:

以下是输出的样子:

1T1b5T
1T2b1T1b2T
1T1b1T2b2T
1T3b1T1b1T
3T3b1T
1T3b1T1b1T
5T1*1T

11X21b1X
4X1b1X

这里有一个C语言的解决方案(是我的朋友解决的,不是我),但我仍然想用JAVA来完成:

#include <stdio.h>

int main (void)
{
    char row[134];
    for (;fgets (row,134,stdin)!=NULL;)
    {
        int i,j=0;
        for (i=0;row[i]!='\0';i++)
        {
            if (row[i]<='9'&&row[i]>='1')
                j+=(row[i]-'0');
            else if ((row[i]<='Z'&&row[i]>='A')||row[i]=='*')
                for (;j;j--)
                    printf ("%c",row[i]);
            else if (row[i]=='b')
                for (;j;j--)
                    printf (" ");
            else if (row[i]=='!'||row[i]=='\n')
                printf ("\n");
        }
    }
    return 0;
} 

1
标题不正确:外部循环没有起作用!那是一个简单的while循环。不是foreach循环。 - Martijn Courteaux
9个回答

15

看起来你做的工作比必要的多。 Scanner可以使用自定义分隔符;在你的情况下,你想让它在换行符或感叹号上分割输入,因此:

Scanner stdin = new Scanner(System.in);
stdin.useDelimiter("[!\\s]*"); // Break on any combination of whitespace characters and !s

while (stdin.hasNext()) {        // While there's a next token,
    String line = stdin.next();  // read it in
    System.out.println(line);    // and print it out.
}

你的打印语句有些奇怪——上面的代码假定你只想打印每一行。在你的代码中,你似乎尝试了两次打印,如果我误解了你的意图,请适当修改。

根据您的更新进行编辑:好的,所以你想允许空令牌并将它们传递。这很容易:只需修改分隔符,使其仅匹配一个字符,就像这样:

stdin.useDelimiter("(!|\\r?\\n|\\r)")
没有星号时,我们只会一次匹配一个字符: 感叹号或者换行符(来自不同操作系统的三种换行符之一)。

2
我认为 stdin.setDelimiter("[!\\s]*"); 应该改为 stdin.useDelimiter("[!\\s]*"); - ant
1
@Etaoin 我修复了那个问题,Eclipse建议的。我更新了我的问题。谢谢。 - Gandalf StormCrow
@Gandalf:根据您修改问题的要求,我已经修订了我的答案。 - Etaoin
@Etaoin 这里 http://pastebin.com/KZifpDQX,我的输出现在看起来像这样,非常奇怪,跳过了很多行。 - Gandalf StormCrow
@Etaoin,你离解决方案很近了,这是我的输出结果:http://pastebin.com/CsFVtMqw .. 只有最后两行应该分开,它们应该像这样:http://pastebin.com/b4H242Mb - Gandalf StormCrow

10

也许是因为你在最后一个输入行后没有按下 enter 键?

更新:我已经在这里测试并确认了。您需要在最后一个输入行后按下 enter 键。只有这样,while (stdin.hasNext()) 才会返回 true 并继续处理该行。


@BalusC 当我按下Enter键时,最后一行 4X1b1X 将在接下来的两行中重复两次。 - Gandalf StormCrow
2
@Gandalf:你的代码确实复制了所有分割部分。这是另一个问题吗?如果是,那么只需摆脱整个第二个for循环。它的作用与System.out.print(line);完全相同(因此没有换行符),但你已经有了一个System.out.println(line); - BalusC
Etaion已经回答了这个问题。请在以后提出更好的问题。你最初的问题暗示了重复是可以预料的:从代码和结果中很明显,但你没有说任何关于它的话。否则我也会回答/评论这一点。 - BalusC
@BalusC 还在等你的回答呢 :D - Gandalf StormCrow
抱歉,我又重新测试了一下,它现在可以正常工作了。我复制了Etaoin的代码示例,并相应地更新了useDelimiter(),在Eclipse中运行它,在控制台中复制粘贴了您的整个字符串,输出结果很好。我甚至用FileInputStream替换了System.in,从文件中读取它,也能正常工作。你的问题出在其他地方。 - BalusC
显示剩余3条评论

3

我已经写了一个答案,那是一个解决方案。但你现在问的是你做错了什么。

  1. You are using Eclipse. This is not bad, but if I try to do this in NetBeans the code works a litle bit else because of you are reading and writing in the same loop. So the when I do copy-paste the input in eclipse, it works as you want. But if I copy-paste line by line, it becomes one big mess. So you have to split the read and the write process.
  2. What you are doing wrong is that you first println the result and then (Why? I don't know. I think you also don't know...) do a print of each char in the result. So the result is that you have only one good line and the others are started by the previous line. So:

    [line 1]: foo
    [line 2]: foobar
    [line 3]: barbaz
    [line 4]: bazbam
    

    You see? So begin with deleting your inner loop that prints each character.
    When you did this: your output looks like this:

    1T1b5T
    1T2b1T1b2T
    1T1b1T2b2T
    1T3b1T1b1T
    3T3b1T
    1T3b1T1b1T
    5T1*1T
    11X21b1X
    4X1b1X
    

    So this is the reason why you thought the foreach loop was the problem. If you look at the end of each outputline (your wrong output: posted in the question), you can see, it was correct.

  3. Then you want in the output a blank line if there was one in the input. But like I wrote in my other answer:

    I hate Scanner

    Scanner does not really read the next line, but the next word. So you have to change it to stdin.nextLine(); Now it works!


最后,这就是你需要的代码:

Scanner stdin = new Scanner(new BufferedInputStream(System.in));
while (stdin.hasNext()) {
    String line = stdin.nextLine();
    for (String part : line.split("!")) {
        System.out.println(part);
    }
}

2
根据Java SE 6文档中java.util.Scanner.next()的说明:
该方法查找并返回此扫描器中的下一个完整标记。完整标记由与分隔符模式匹配的输入前缀和后缀组成。即使hasNext()的先前调用返回了true,此方法可能会阻塞等待扫描的输入。
我猜测您的Java代码中发生的情况是,在最后一行之后,应用程序正在阻塞等待换行符。

1
public static List<String> ppp(final String source) {
    final ArrayList<String> result = new ArrayList<String>();
    final Scanner scan = new Scanner(source);
    scan.useDelimiter("!");
    while (scan.hasNext()) {
    result.add(scan.next());
    }
    return result;
}

private static void printout(final List<String> someArgs) {
    final Iterator<String> i = someArgs.iterator();
    while (i.hasNext()) {
    System.out.println(i.next());
    }
} 
public static void main(final String[] args) throws IOException {
    final String lineSeparator = System.getProperty("line.separator");
    final String test = "1T1b5T!1T2b1T1b2T!1T1b1T2b2T!1T3b1T1b1T!3T3b1T!1T3b1T1b1T!5T1*1T"
            + lineSeparator + lineSeparator + "11X21b1X" + lineSeparator + "4X1b1X" + lineSeparator;
    System.out.println("Source: ");
    System.out.println(test);
    System.out.println("Result as List: ");
    System.out.println(ppp(test));
    System.out.println("Result in lines:");
    printout(ppp(test));}

String test是一个包含LineSeparators的多行字符串。

方法ppp返回一个由元素用"!"分隔的数组。

方法prinout只是将数组的元素以单独的行打印到stdout中。

整个main()生成以下输出:

源码: 1T1b5T!1T2b1T1b2T!1T1b1T2b2T!1T3b1T1b1T!3T3b1T!1T3b1T1b1T!5T1*1T
11X21b1X 4X1b1X
结果列表: [1T1b5T, 1T2b1T1b2T, 1T1b1T2b2T, 1T3b1T1b1T, 3T3b1T, 1T3b1T1b1T, 5T1*1T
11X21b1X 4X1b1X ]
结果按行显示: 1T1b5T 1T2b1T1b2T 1T1b1T2b2T 1T3b1T1b1T 3T3b1T 1T3b1T1b1T 5T1*1T
11X21b1X 4X1b1X

0

对我来说,无论是否有尾随换行符,它都有效。当然,“有效”的定义不清楚,除了你期望看到最后一行“4X1b1X”,我假设。我确实看到了。

C:\>java Main < test.txt
1T1b5T
1T1b5T1T2b1T1b2T
1T2b1T1b2T1T1b1T2b2T
1T1b1T2b2T1T3b1T1b1T
1T3b1T1b1T3T3b1T
3T3b1T1T3b1T1b1T
1T3b1T1b1T5T1*1T
5T1*1T11X21b1X
11X21b1X4X1b1X
4X1b1X

@mrjoltcola 是的,它“可用”,但它的输出不像我想要实现的那样,我更新了我的问题。谢谢。 - Gandalf StormCrow

0

我不知道你在做什么。你正在同时读取和打印...(所以我分开了输入和输出)

个人而言,我讨厌Scanner类。我总是使用BufferedReader。

因此,我首先将输入存储在列表中。这样用户就可以在没有侵略性输出的情况下提供输入。当你有了输入,你可以简单地打印它。

我像下面这样做,它可以正确工作:

public static void main(String[] args) throws IOException {
    List<String> lines = new ArrayList<String>();
    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    System.out.print("How many lines count the input? ");
    int count = Integer.parseInt(reader.readLine());
    for (int i = 0; i < count; i++)
    {
        lines.add(reader.readLine());
    }
    System.out.println("\nNow comes the output:");
    for (int i = 0; i < count; i++)
    {
        String line = lines.get(i);
        String[] splitted = line.split("!");
        for (String string : splitted) {
            System.out.println(string);
        }
    }
}

这是我运行它的方式:

How many lines count the input? 4
1T1b5T!1T2b1T1b2T!1T1b1T2b2T!1T3b1T1b1T!3T3b1T!1T3b1T1b1T!5T1*1T

11X21b1X
4X1b1X


Now comes the output:
1T1b5T
1T2b1T1b2T
1T1b1T2b2T
1T3b1T1b1T
3T3b1T
1T3b1T1b1T
5T1*1T

11X21b1X
4X1b1X
BUILD SUCCESSFUL (total time: 10 seconds)

0
问题出在扫描器上。看起来你只想读取行。对于这个,你不需要一个扫描器(因为它不会返回未分隔的最后一部分)。只需使用缓冲读取器的readLine即可:
import java.io.BufferedInputStream;
import java.util.Scanner;

public class Main {

    public static void main(String args[]) {
        BufferedInputStream stdin = new Scanner(new BufferedInputStream(System.in));
        String line;
        while ((line=stdin.nextLine)!=null) {
            for (String token : line.split("!")) {
                System.out.println(token);
            }
        }
    }

}

0

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