06.2026
This commit is contained in:
@@ -0,0 +1,216 @@
|
||||
package sparseviewer.io;
|
||||
|
||||
import sparseviewer.model.ScanDataset;
|
||||
import ij.IJ;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
public class TxtTableReader {
|
||||
|
||||
public static ScanDataset readFiles(List<File> files) throws IOException {
|
||||
if (files == null || files.isEmpty()) {
|
||||
throw new IOException("No files selected.");
|
||||
}
|
||||
|
||||
String[] referenceHeader = null;
|
||||
|
||||
List<double[]> allRows = new ArrayList<double[]>();
|
||||
List<Integer> fileIndices = new ArrayList<Integer>();
|
||||
|
||||
for (int fileIdx = 0; fileIdx < files.size(); fileIdx++) {
|
||||
File file = files.get(fileIdx);
|
||||
ParsedTxt parsed = readSingleFile(file);
|
||||
|
||||
if (referenceHeader == null) {
|
||||
referenceHeader = parsed.channelNames;
|
||||
} else {
|
||||
if (!sameHeader(referenceHeader, parsed.channelNames)) {
|
||||
throw new IOException(
|
||||
"Header mismatch in file:\n" + file.getAbsolutePath()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < parsed.rows.size(); i++) {
|
||||
allRows.add(parsed.rows.get(i));
|
||||
fileIndices.add(Integer.valueOf(fileIdx));
|
||||
}
|
||||
}
|
||||
|
||||
int rowCount = allRows.size();
|
||||
int colCount = referenceHeader.length;
|
||||
|
||||
double[][] columns = new double[colCount][rowCount];
|
||||
int[] fileIndex = new int[rowCount];
|
||||
|
||||
for (int r = 0; r < rowCount; r++) {
|
||||
double[] row = allRows.get(r);
|
||||
|
||||
for (int c = 0; c < colCount; c++) {
|
||||
columns[c][r] = row[c];
|
||||
}
|
||||
|
||||
fileIndex[r] = fileIndices.get(r).intValue();
|
||||
}
|
||||
|
||||
IJ.log("Sparse Scan Viewer: loaded " + files.size()
|
||||
+ " file(s), " + rowCount
|
||||
+ " rows, " + colCount + " channels.");
|
||||
|
||||
return new ScanDataset(referenceHeader, columns, rowCount, fileIndex, files);
|
||||
}
|
||||
|
||||
private static ParsedTxt readSingleFile(File file) throws IOException {
|
||||
BufferedReader br = null;
|
||||
|
||||
try {
|
||||
br = new BufferedReader(new FileReader(file));
|
||||
|
||||
String line;
|
||||
String[] channels = null;
|
||||
int expectedRows = -1;
|
||||
List<double[]> rows = new ArrayList<double[]>();
|
||||
|
||||
while ((line = br.readLine()) != null) {
|
||||
String trimmed = line.trim();
|
||||
|
||||
if (trimmed.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (trimmed.startsWith("#table[")) {
|
||||
HeaderInfo info = parseHeader(trimmed);
|
||||
channels = info.channelNames;
|
||||
expectedRows = info.expectedRows;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (trimmed.startsWith("#")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (channels == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String[] parts = trimmed.split(";");
|
||||
|
||||
if (parts.length < channels.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double[] row = new double[channels.length];
|
||||
boolean valid = true;
|
||||
|
||||
for (int i = 0; i < channels.length; i++) {
|
||||
try {
|
||||
row[i] = Double.parseDouble(parts[i].trim());
|
||||
} catch (NumberFormatException e) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
rows.add(row);
|
||||
}
|
||||
}
|
||||
|
||||
if (channels == null) {
|
||||
throw new IOException("No #table[...] header found in " + file.getName());
|
||||
}
|
||||
|
||||
if (expectedRows >= 0 && expectedRows != rows.size()) {
|
||||
IJ.log("Warning: " + file.getName()
|
||||
+ " header says " + expectedRows
|
||||
+ " rows, but parsed " + rows.size() + " rows.");
|
||||
}
|
||||
|
||||
ParsedTxt parsed = new ParsedTxt();
|
||||
parsed.channelNames = channels;
|
||||
parsed.rows = rows;
|
||||
return parsed;
|
||||
|
||||
} finally {
|
||||
if (br != null) {
|
||||
try {
|
||||
br.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static HeaderInfo parseHeader(String line) throws IOException {
|
||||
int open = line.indexOf('[');
|
||||
int close = line.indexOf(']');
|
||||
|
||||
if (open < 0 || close < 0 || close <= open) {
|
||||
throw new IOException("Invalid #table header:\n" + line);
|
||||
}
|
||||
|
||||
int expectedRows = -1;
|
||||
|
||||
try {
|
||||
expectedRows = Integer.parseInt(line.substring(open + 1, close).trim());
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
String rest = line.substring(close + 1).trim();
|
||||
String[] rawChannels = rest.split(";");
|
||||
|
||||
Vector<String> names = new Vector<String>();
|
||||
|
||||
for (int i = 0; i < rawChannels.length; i++) {
|
||||
String name = rawChannels[i].trim();
|
||||
|
||||
if (name.length() > 0) {
|
||||
names.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (names.isEmpty()) {
|
||||
throw new IOException("No channels found in header:\n" + line);
|
||||
}
|
||||
|
||||
HeaderInfo info = new HeaderInfo();
|
||||
info.expectedRows = expectedRows;
|
||||
info.channelNames = names.toArray(new String[names.size()]);
|
||||
return info;
|
||||
}
|
||||
|
||||
private static boolean sameHeader(String[] a, String[] b) {
|
||||
if (a == null || b == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (a.length != b.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < a.length; i++) {
|
||||
if (!a[i].equals(b[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static class HeaderInfo {
|
||||
int expectedRows;
|
||||
String[] channelNames;
|
||||
}
|
||||
|
||||
private static class ParsedTxt {
|
||||
String[] channelNames;
|
||||
List<double[]> rows;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user