逐行迭代文本文件的内容 - 是否有最佳实践? (与PMD的AssignmentInOperand相比)

发布于 2024-10-12 05:39:59 字数 630 浏览 4 评论 0 原文

我们有一个 Java 应用程序,它有一些能够读取文本文件的模块。他们使用这样的代码非常简单:

BufferedReader br = new BufferedReader(new FileReader(file));  
String line = null;  
while ((line = br.readLine()) != null)  
{  
   ... // do stuff to file here  
} 

我在我的项目上运行了PMD,并在while (...)<中遇到了“AssignmentInOperand”违规。 /代码>行。

除了显而易见的方法之外,是否还有更简单的方法来执行此循环:

String line = br.readLine();  
while (line != null)  
{  
   ... // do stuff to file here  
   line = br.readLine();  
} 

这是否被认为是更好的实践? (尽管我们“复制”了 line = br.readLine() 代码?)

We have a Java Application that has a few modules that know to read text files. They do it quite simply with a code like this:

BufferedReader br = new BufferedReader(new FileReader(file));  
String line = null;  
while ((line = br.readLine()) != null)  
{  
   ... // do stuff to file here  
} 

I ran PMD on my project and got the 'AssignmentInOperand' violation on the while (...) line.

Is there a simpler way of doing this loop other than the obvious:

String line = br.readLine();  
while (line != null)  
{  
   ... // do stuff to file here  
   line = br.readLine();  
} 

Is this considered a better practice? (although we "duplicate" the line = br.readLine() code?)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

跨年 2024-10-19 05:39:59

我知道这是一篇旧帖子,但我(几乎)有同样的需求,我使用 Apache Commons 中 FileUtils 的 LineIterator 解决了它。
从他们的 javadoc:

LineIterator it = FileUtils.lineIterator(file, "UTF-8");
try {
    while (it.hasNext()) {
    String line = it.nextLine();
    // do something with line
    }
} finally {
    it.close();
}

检查文档:
http://commons .apache.org/proper/commons-io/javadocs/api-release/org/apache/commons/io/LineIterator.html

I know is an old post but I just had the same need (almost) and I solve it using a LineIterator from FileUtils in Apache Commons.
From their javadoc:

LineIterator it = FileUtils.lineIterator(file, "UTF-8");
try {
    while (it.hasNext()) {
    String line = it.nextLine();
    // do something with line
    }
} finally {
    it.close();
}

Check the documentation:
http://commons.apache.org/proper/commons-io/javadocs/api-release/org/apache/commons/io/LineIterator.html

过潦 2024-10-19 05:39:59

java-8 和 Try-With-Resources 允许您以更紧凑的语法实现您想要的效果。

Path path = Paths.get("c:/users/aksel/aksel.txt");

try (Stream<String>  lines = Files.lines(path)) {
    lines.forEachOrdered(line->System.out.println(line));
} catch (IOException e) {
    //error happened
}

The support for streams and Lambdas in java-8 and Try-With-Resources of java-7 allows you to achive what you want in more compact syntax.

Path path = Paths.get("c:/users/aksel/aksel.txt");

try (Stream<String>  lines = Files.lines(path)) {
    lines.forEachOrdered(line->System.out.println(line));
} catch (IOException e) {
    //error happened
}
笔落惊风雨 2024-10-19 05:39:59

我经常使用 while((line = br.readLine()) != null) 构造...但是,最近我遇到了这个不错的替代方案

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

for (String line = br.readLine(); line != null; line = br.readLine()) {
   ... // do stuff to file here  
}

这仍然是重复 readLine() 调用代码,但逻辑很清晰,等等。

另一次我使用 while(( ... ) ...) 构造是从流读取到 byte[] 数组时...

byte[] buffer = new byte[size];
InputStream is = .....;
int len = 0;
while ((len = is.read(buffer)) >= 0) {
    ....
}

这也可以转换为 for 循环with:

byte[] buffer = new byte[size];
InputStream is = .....;
for (int len = is.read(buffer); len >= 0; len = is.read(buffer)) {
    ....
}

我不确定我是否真的更喜欢 for 循环替代方案......但是,它会满足任何 PMD 工具,并且逻辑仍然清晰,等等。

I routinely use the while((line = br.readLine()) != null) construct... but, recently I came accross this nice alternative:

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

for (String line = br.readLine(); line != null; line = br.readLine()) {
   ... // do stuff to file here  
}

This is still duplicating the readLine() call code, but the logic is clear, etc.

The other time I use the while(( ... ) ...) construct is when reading from a stream in to a byte[] array...

byte[] buffer = new byte[size];
InputStream is = .....;
int len = 0;
while ((len = is.read(buffer)) >= 0) {
    ....
}

This can also be transformed in to a for loop with:

byte[] buffer = new byte[size];
InputStream is = .....;
for (int len = is.read(buffer); len >= 0; len = is.read(buffer)) {
    ....
}

I am not sure I really prefer the for-loop alternatives.... but, it will satisfy any PMD tool, and the logic is still clear, etc.

半夏半凉 2024-10-19 05:39:59

我一般更喜欢前者。我一般不喜欢比较中的副作用,但这个特定的例子是一个惯用语,它是如此常见和方便,以至于我不反对它。

(在 C# 中,有一个更好的选择:返回一个 IEnumerable 的方法,您可以使用 foreach 对其进行迭代;这在 Java 中没有那么好,因为在末尾没有自动处置增强的 for 循环...而且还因为您不能从迭代器中抛出 IOException,这意味着您不能只是将其中一个替换为另一个。

)方式:重复行问题比操作数内赋值问题更困扰我。我习惯于一目了然地接受这种模式 - 对于重复的行版本,我需要停下来检查一切是否都在正确的位置。这可能是一种习惯,但我不认为这是一个问题。

I generally prefer the former. I don't generally like side-effects within a comparison, but this particular example is an idiom which is so common and so handy that I don't object to it.

(In C# there's a nicer option: a method to return an IEnumerable<string> which you can iterate over with foreach; that isn't as nice in Java because there's no auto-dispose at the end of an enhanced for loop... and also because you can't throw IOException from the iterator, which means you can't just make one a drop-in replacement for the other.)

To put it another way: the duplicate line issue bothers me more than the assignment-within-operand issue. I'm used to taking in this pattern at a glance - with the duplicate line version I need to stop and check that everything's in the right place. That's probably habit as much as anything else, but I don't think it's a problem.

铜锣湾横着走 2024-10-19 05:39:59

根据 Jon 的回答,我认为创建一个装饰器来充当文件迭代器应该很容易,这样您就可以使用 foreach 循环:

public class BufferedReaderIterator implements Iterable<String> {

    private BufferedReader r;

    public BufferedReaderIterator(BufferedReader r) {
        this.r = r;
    }

    @Override
    public Iterator<String> iterator() {
        return new Iterator<String>() {

            @Override
            public boolean hasNext() {
                try {
                    r.mark(1);
                    if (r.read() < 0) {
                        return false;
                    }
                    r.reset();
                    return true;
                } catch (IOException e) {
                    return false;
                }
            }

            @Override
            public String next() {
                try {
                    return r.readLine();
                } catch (IOException e) {
                    return null;
                }
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }

        };
    }

}

公平警告:这会抑制读取期间可能发生的 IOExceptions,并简单地停止读取过程。目前还不清楚 Java 中是否有一种方法可以解决这个问题,而不会引发运行时异常,因为迭代器方法的语义已明确定义,并且必须遵守才能使用 for-each 语法。另外,在这里运行多个迭代器会产生一些奇怪的行为;所以我不确定这是否值得推荐。

不过,我确实对此进行了测试,并且确实有效。

无论如何,您可以使用 for-each 语法作为一种装饰器来受益:

for(String line : new BufferedReaderIterator(br)){
    // do some work
}

Based on Jon's answer I got to thinking it should be easy enough to create a decorator to act as a file iterator so you can use a foreach loop:

public class BufferedReaderIterator implements Iterable<String> {

    private BufferedReader r;

    public BufferedReaderIterator(BufferedReader r) {
        this.r = r;
    }

    @Override
    public Iterator<String> iterator() {
        return new Iterator<String>() {

            @Override
            public boolean hasNext() {
                try {
                    r.mark(1);
                    if (r.read() < 0) {
                        return false;
                    }
                    r.reset();
                    return true;
                } catch (IOException e) {
                    return false;
                }
            }

            @Override
            public String next() {
                try {
                    return r.readLine();
                } catch (IOException e) {
                    return null;
                }
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }

        };
    }

}

Fair warning: this suppresses IOExceptions that might occur during reads and simply stops the reading process. It's unclear that there's a way around this in Java without throwing runtime exceptions as the semantics of the iterator methods are well defined and must be conformed to in order to use the for-each syntax. Also, running multiple iterators here would have some strange behavior; so I'm not sure this is recommended.

I did test this, though, and it does work.

Anyway, you get the benefit of for-each syntax using this as a kind of decorator:

for(String line : new BufferedReaderIterator(br)){
    // do some work
}
瑕疵 2024-10-19 05:39:59

我有点惊讶没有提到以下替代方案:

while( true ) {
    String line = br.readLine();
    if ( line == null ) break;
    ... // do stuff to file here
}

在 Java 8 之前,它是我最喜欢的,因为它清晰且不需要重复。 IMO,break 是具有副作用的表达式的更好选择。不过,这仍然是一个习语问题。

I'm a bit surprised the following alternative was not mentioned:

while( true ) {
    String line = br.readLine();
    if ( line == null ) break;
    ... // do stuff to file here
}

Before Java 8 it was my favorite because of its clarity and not requiring repetition. IMO, break is a better option to expressions with side-effects. It's still a matter of idioms, though.

素染倾城色 2024-10-19 05:39:59

Google 的 Guava Libraries 提供了使用静态方法的替代解决方案CharStreams.readLines(Readable, LineProcessor) 以及用于处理每一行的 LineProcessor 实现。

try (BufferedReader br = new BufferedReader(new FileReader(file))) {
    CharStreams.readLines(br, new MyLineProcessorImpl());
} catch (IOException e) {
    // handling io error ...
}

while 循环的主体现在放置在 LineProcessor 实现中。

class MyLineProcessorImpl implements LineProcessor<Object> {

    @Override
    public boolean processLine(String line) throws IOException {
        if (// check if processing should continue) {
            // do sth. with line
            return true;
        } else {
            // stop processing
            return false;
        }
    }

    @Override
    public Object getResult() {
        // return a result based on processed lines if needed
        return new Object();
    }
}

Google's Guava Libraries provide an alternative solution using the static method CharStreams.readLines(Readable, LineProcessor<T>) with an implementation of LineProcessor<T> for processing each line.

try (BufferedReader br = new BufferedReader(new FileReader(file))) {
    CharStreams.readLines(br, new MyLineProcessorImpl());
} catch (IOException e) {
    // handling io error ...
}

The body of the while loop is now placed in the LineProcessor<T> implementation.

class MyLineProcessorImpl implements LineProcessor<Object> {

    @Override
    public boolean processLine(String line) throws IOException {
        if (// check if processing should continue) {
            // do sth. with line
            return true;
        } else {
            // stop processing
            return false;
        }
    }

    @Override
    public Object getResult() {
        // return a result based on processed lines if needed
        return new Object();
    }
}
余生共白头 2024-10-19 05:39:59

在 PMD 中,AssignmentInOperand 是一个有争议的规则,该规则的原因是:“这会使代码更加复杂且难以阅读”(请参考 http://pmd.sourceforge.net/rules/controversial.html)

如果您确实想这样做,您可以禁用该规则。就我而言,我更喜欢前者。

AssignmentInOperand is a controversial rule in PMD, the reason of this rule is: "this can make code more complicated and harder to read" (please refer http://pmd.sourceforge.net/rules/controversial.html)

You could disable that rule if you really want to do it that way. In my side I prefer the former.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文