Остерегайтесь разницы между прямым использованием _1 _ / OutputStreamWriter
и их подклассы и _3 _ / _ 4_ фабричные методы _5 _ а>. Если в первом случае используется системная кодировка по умолчанию, когда не указана явная кодировка, то во втором всегда по умолчанию используется UTF-8
. Поэтому я настоятельно рекомендую всегда указывать желаемую кодировку, даже если это Charset.defaultCharset()
или StandardCharsets.UTF_8
, чтобы задокументировать ваше намерение и избежать сюрпризов, если вы переключаетесь между различными способами создания Reader
или Writer
.
Если вы хотите разделить по границам строки, нет никакого способа обойтись без просмотра содержимого файла. Поэтому вы не можете оптимизировать его так, как как при слиянии.
Если вы готовы пожертвовать переносимостью, вы можете попробовать несколько оптимизаций. Если вы знаете, что кодировка кодировки будет однозначно отображать '\n'
в (byte)'\n'
, как это имеет место для большинства однобайтовых кодировок, а также для UTF-8
, вы можете сканировать разрывы строк на уровне байтов, чтобы получить позиции файлов для разделения и избежать любой передачи данных из вашего приложения в систему ввода-вывода.
public void splitTextFiles(Path bigFile, int maxRows) throws IOException {
MappedByteBuffer bb;
try(FileChannel in = FileChannel.open(bigFile, READ)) {
bb=in.map(FileChannel.MapMode.READ_ONLY, 0, in.size());
}
for(int start=0, pos=0, end=bb.remaining(), i=1, lineNum=1; pos<end; lineNum++) {
while(pos<end && bb.get(pos++)!='\n');
if(lineNum < maxRows && pos<end) continue;
Path splitFile = Paths.get(i++ + "split.txt");
// if you want to overwrite existing files use CREATE, TRUNCATE_EXISTING
try(FileChannel out = FileChannel.open(splitFile, CREATE_NEW, WRITE)) {
bb.position(start).limit(pos);
while(bb.hasRemaining()) out.write(bb);
bb.clear();
start=pos;
lineNum = 0;
}
}
}
Недостатком является то, что он не работает с кодировками типа UTF-16
или EBCDIC
и, в отличие от BufferedReader.readLine()
, не поддерживает одиночный '\r'
в качестве терминатора строки, как в старой MacOS9.
Кроме того, он поддерживает только файлы размером менее 2 ГБ; предел, вероятно, еще меньше на 32-битных JVM из-за ограниченного виртуального адресного пространства. Для файлов, размер которых превышает установленный предел, необходимо будет перебирать фрагменты исходного файла и map
их один за другим.
Эти проблемы можно исправить, но это повысит сложность этого подхода. Учитывая тот факт, что прирост скорости на моей машине составляет всего около 15% (я не ожидал большего, поскольку здесь преобладает ввод-вывод) и будет даже меньше, когда возрастет сложность, я не думаю, что оно того стоит.
Суть в том, что для этой задачи подход _20 _ / _ 21_ достаточно, но вы должны позаботиться о Charset
, используемом для операции.
person
Holger
schedule
29.08.2014