Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion code/src/main/java/com/codeforces/commons/io/FileUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,27 @@ public static String readFile(File file) throws IOException {
return Objects.requireNonNull(executeIoOperation(() -> UnsafeFileUtil.readFile(file)));
}

/**
* Reads a file, limiting the number of lines read and cropping each line
* if its length exceeds the maximum.
* <p>
* This implementation is efficient because it uses Files.lines() for
* lazy, buffered, line-by-line processing, avoiding loading the entire
* file into memory. The .limit() operation short-circuits reading,
* stopping disk access as soon as maxLineCount is reached.
*
* @param file File to be read.
* @param maxLineCount Maximum number of lines to read.
* @param maxLineLength Maximum length of each line to crop to.
* @return String containing file data in cropped form.
* @throws IOException if can't read file (e.g., file doesn't exist, is a directory, or permission issues).
*/
@Nonnull
public static String readFileCropped(File file, int maxLineCount, int maxLineLength) throws IOException {
return Objects.requireNonNull(executeIoOperation(()
-> UnsafeFileUtil.readFileCropped(file, maxLineCount, maxLineLength)));
}

/**
* Writes new file into filesystem. Overwrite existing if exists.
* Creates parent directory if needed.
Expand Down Expand Up @@ -1049,7 +1070,7 @@ private static long parseSize(@Nonnull String size, @Nonnegative int lastCharInd
/**
* Set executable permission for file.
*
* @param file File to set permission.
* @param file File to set permission.
* @param executable {@code true} to set executable permission.
*/
public static void setExecutable(@Nonnull File file, boolean executable) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.codeforces.commons.lang.ObjectUtil;
import com.codeforces.commons.math.RandomUtil;
import com.codeforces.commons.properties.internal.CommonsPropertiesUtil;
import com.codeforces.commons.text.StringUtil;
import com.google.common.primitives.Ints;
import com.google.errorprone.annotations.MustBeClosed;
import de.schlichtherle.truezip.file.TFile;
Expand All @@ -32,6 +33,8 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.codeforces.commons.math.Math.max;

Expand Down Expand Up @@ -879,6 +882,54 @@ public static byte[] downloadFileAsByteArray(
return IoUtil.toByteArray(connection.getInputStream(), maxSize);
}

@Nonnull
public static String readFileCropped(File file, int maxLineCount, int maxLineLength) throws IOException {
if (maxLineCount <= 0) {
return "";
}

List<String> lines;
boolean moreLinesExist;

// 1. Read one line more than the limit (maxLineCount + 1)
// try-with-resources ensures the underlying file handle/buffer is closed immediately.
try (Stream<String> linesStream = Files.lines(file.toPath())) {

// Collect up to maxLineCount + 1 lines. The .limit() operation short-circuits reading.
lines = linesStream
.limit(maxLineCount + 1)
// Apply line-level cropping/shrinking
.map(line -> StringUtil.shrinkTo(line, maxLineLength))
.collect(Collectors.toList());
}

// 2. Check if the (maxLineCount + 1)-th line was present
if (lines.size() > maxLineCount) {
moreLinesExist = true;
// Remove the extra line so it's not included in the output
lines.remove(maxLineCount);
} else {
moreLinesExist = false;
}

// 3. Assemble the final result string
String lineSeparator = System.lineSeparator();

// Join the processed lines
String result = String.join(lineSeparator, lines);

// Conditionally append the ellipsis line if the file had more content
if (moreLinesExist) {
// Append line separator only if there are existing lines
if (!result.isEmpty()) {
result += lineSeparator;
}
result += "...";
}

return result;
}

@SuppressWarnings({"FinalizeDeclaration", "DeserializableClassInSecureContext"})
private static class TemporaryDirectory extends File {
private TemporaryDirectory(String pathname) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1370,7 +1370,8 @@ public static String cropLines(String input, int maxLineLength, int maxLineNumbe
return cropLines(input, maxLineLength, maxLineNumber, "...", "...");
}

public static String cropLines(String input, int maxLineLength, int maxLineNumber, String endOfLineReplacement, String endOfContentReplacement) {
public static String cropLines(String input, int maxLineLength, int maxLineNumber,
String endOfLineReplacement, String endOfContentReplacement) {
if (isEmpty(input)) {
return input;
}
Expand Down