Разделить файл на несколько файлов, если он достигает определенного предела?


Для каждого clientIdУ меня Collection<Task> объект. Я делаю файл для каждого clientId и для каждого clientId файл, я уже все свои задачи в ней разделены новой строкой в нем.

Ниже мой оригинальный код:-

public class FileProcessor {
  private final String clientId;
  private final Collection<Task> tasks;

  public FileProcessor(String clientId, Collection<Task> tasks) {
    this.clientId = clientId;
    this.tasks = tasks;
  }

  public void generateFile() {
    String fileName = "tasks_info_" + clientId + ".txt";
    StringBuilder sb = new StringBuilder();
    for (Task task : tasks) {
      sb.append(task).append(System.getProperty("line.separator"));
    }
    try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {
      writer.write(sb.toString());
    } catch (IOException ex) {
      ex.printStackTrace();
    }
  }
}

Скажем, если clientId это 123тогда будет создан файл с названием "tasks_info_123.txt" все задачи в нем. Теперь у меня есть требование, что размер каждого clientId файл не может идти более 50000 байт. Так что теперь мне нужно сделать, это:

  • Я должен разделить каждый clientId файл на несколько файлов такой: "tasks_info_123_0.txt", "tasks_info_123_1.txt", "tasks_info_123_2.txt"
  • Как вы можете видеть, я добавил "_0", "_1", "_2" в нем.
  • Так что я буду продолжать создавать несколько файлов для каждого clientId пока я не могу уместить все данные в нем, а также убедившись, что каждый файл размером не более 50000 байт.
  • Если мы можем поместить все данные в один файл, то у меня будет только один clientId файл вроде этого: "tasks_info_123_0.txt"
  • Также мы не должны создать файл, если объект задачи пустая.

Так что я получил ниже код, но это не эффективно так же кажется, поэтому выбирают для проверки кода, чтобы увидеть, если есть какие-либо лучше и эффективным способом?

  public void generateFile() {
    int size = 0;
    int index = 0;
    StringBuilder sb = new StringBuilder();
    for (Task task : tasks) {
      sb.append(task).append(System.getProperty("line.separator"));
      size += sb.toString().getBytes(StandardCharsets.UTF_8).length;
      if (size > 50000) {
        String fileName = "tasks_info_" + clientId + "_" + index + ".txt";
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {
          writer.write(sb.toString());
        } catch (IOException ex) {
          ex.printStackTrace();
        }
        index++;
        sb = new StringBuilder();
        size = 0;
      }
    }
    // for cases where we don't reach the limit
    String fileName = "tasks_info_" + clientId + "_" + index + ".txt";
    try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {
      writer.write(sb.toString());
    } catch (IOException ex) {
      ex.printStackTrace();
    }
  }

Примечание: Я не хочу использовать регистратор для этого, просто я хочу попробовать его на мою собственную.



Комментарии
1 ответ

Вы хотите ограничить размер файла, измеряется в байтах.
Вы пишите записи, по одной для каждого taskи заканчивается line.separator.
Вы беспокоитесь о производительности (с лимитом всего 50 кб размер файла - ну да ладно).


  • не кодируют charС более чем один раз (с (согласно: 50) записей в файле, ваш средний char получает закодированный Н+1 (26) раза со второго generateFile() представил)

  • используя "старые Ява ИО", есть FilterOutputStream вяжутся байт

  • не используйте буферизованную Writer: Она будет скрывать записи границы

дать ему попробовать:

/** Writes a limited amount of bytes to each of
* a succession of files numbered from 0 where necessary.
* Buffers liberally. */
static class SequentialFileOutputStream extends java.io.FilterOutputStream {
final static int MAXBUF = 1<<20;
final byte[]buffer;
int pos, index;
final long limit;
long left;
File file;
String path, name, suffix;
/** Creates an OutputStream that starts writing
* to <code>file
создание нумерованных файлов по мере необходимости. */
общественные
SequentialFileOutputStream(файл файл, логическое добавление длительный срок)
бросает IOException
{
супер(нулем);
Файл родителя = (это.файл = файл).getParentFile();
если (нуль == родителя)
родитель = новый файл ("".);
// путь, чеки тоже
если (родитель.getFreeSpace() <предел)
бросить(новый Java.Ио.Класс IOException("свободный <лимит"));
это.лимит = слева = пределу;
буфер = новый байт[(инт) математика.мин(предел, MAXBUF)];
}
/** Закрывает текущую часть, если таковые имеются. */
пустота closePart() бросает IOException {
если (значение null != вне) попробуйте {
метод flush();
} наконец, { попробовать {
из.закрыть();
} наконец, {
выход = значение null;
}}
}
/** Завершает текущую часть, если таковые имеются.
* Подготавливает новое по мере необходимости. */
пустота nextPart() бросает IOException {
closePart();
}
/** Обеспечивает out открыт и буфер сбрасывается. */
пустота ensureOpen() бросает исключение filenotfoundexception {
если (значение null != вне)
возврат;
слева = пределу;
поз = 0;
если (нуль == суффикс) {
путь = файл.getPath();
имя = файл.метод getname();
инт точка = имя.lastIndexOf('.');
если (0 <= точка) {
суффикс = имя.подстрока(точка);
путь = путь.подстрока(0,
путь.Длина (м) - суффикс.длина()) + '_';
} еще
суффикс = "";
из = Новый Java.Ио.Поток(файл);
} еще {
если (значение null != файла) {
Нулевой файл = новый файл(путь + '0' + суффикс);
ноль.удалить();
файл.renameTo(ноль);
файл = значение null;
}
из = Новый Java.Ио.Поток(путь + ++индекс + суффикс);
}
}
@Переопределить
общественного недействительными записи(инт б) бросает IOException {
байт[]а = { (байт) б };
писать(а, 0, а.длина);
}
@Переопределить
общественного недействительными записи(байт[] б, инт выкл, инт лен)
бросает IOException {
если (лен <= 0 || значение null == б)
возврат;
ensureOpen();
если (слева < лен && 0 < пос) {
метод flush();
nextPart();
}
если (предел <лен)
из.писать(Б, с, лен);
еще {
Системы.arraycopy(б, с, буфером, пос., лен);
пос += лен;
слева -= лен;
}
}
@Переопределить // closePart()?
общественного недействительными метод flush() бросает IOException {
если (0 < пос) {
ensureOpen();
из.записи(буфера, 0, поз);
поз = 0;
}
из.метод flush();
}
@Переопределить
общественного недействительными закрыть() бросает IOException { closePart(); }
}
частная окончательной Строковый параметр ClientID;
окончательный частная коллекция задачи;

общественность обычно(Строковый параметр ClientID, коллекция задач) {
это.параметр ClientID = параметр ClientID;
это.задачи = задачи;
}
/** Параметр промыть звено outputstreamwriter датчика буфера */
статические FlushWriter классе расширяется Java.Ио.Звено outputstreamwriter {
окончательный статический класс[]NO_CLASSES = {};
статические Ява.яз.отразить.FlushEncoder способ;
// дальнейшего конструкторы оставил в качестве упражнения
общественные FlushWriter(перенаправляет наружу) { супер(выход); }
/** Ает энкодера буфера */
Ява.Ио.FlushEncoder писатель() {
попробовать {
если (нуль == flushEncoder) {
заключительный класс osWriter = FlushWriter.класс.getSuperclass();
flushEncoder = osWriter
.getDeclaredMethod("flushBuffer", NO_CLASSES);
flushEncoder.setAccessible(true);в
}
flushEncoder.вызов(это, (объект[])нуль);
} поймать (ReflectiveOperationException е) {
// Todo автоматически сгенерирован блок catch
е.печатные();
}
возвращение этого;
}
}

generateFile общественного недействительными() {
Строке filename = "tasks_info_" + параметр ClientID + ".тхт",
lineSeparator = система.метод getproperty("линии.разделитель");
попробовать (FlushWriter писатель = новый FlushWriter(
новый SequentialFileOutputStream(
новый файл(новый файл("."), именем), ложного, 9000))) {
для (задач задач : задач) {
писатель.добавить(задач.метод toString()).добавить(lineSeparator);
писатель.flushEncoder();
}
} поймать (исключение IOException экс) {
экс.печатные();
}
}

общественности статической силы основных(string[] аргументы) {
Задача T = новая задача() {@переопределить общественная строка toString() {
возвращение "я самая бесполезная задача когда-нибудь";
}};
Коллекциязадачи = новый Java.утиль.Класса ArrayList<>(999);
для (int я = 999 ; 0 < ... я ; )
задачи.добавить(Т);
новые обычно("сломался", задачи).generateFile();
}

В небуферизированная OutputStreamWriter не хватило, в итоге:
Шифратор каждый буферы. Пытаясь сохранить, что от вмешательства облажалась.

В generateFile() из вопроса кажется, есть правильность проблему с тем, как она прикидок значение: оно расширяет sb и набегает "длина byte[]ы получили от String представления sb". Надо скорее


  • установить размер для этой длины - или -

  • накопить "байт длину" String для каждого Task
    что в сочетании с инстанцирования sb при соответствующей длины, должна привести к времени выполнения линейных количества байт в каждом файле, а не квадратичной.

Но как только вы "имеете byte[]"вы могли бы также собрать их.
Это похоже на призыв к java.nio.ByteBuffer и co:

public void generateFile() {
ByteBuffer bb = ByteBuffer.allocate(limit);
for (Task task : tasks) {
byte[]taskBytes = task.toString().getBytes(
java.nio.charset.StandardCharsets.UTF_8);
int taskLen = taskBytes.length+LS_LENGTH;
if (bb.remaining() < taskLen)
buffer2file(bb, index++);
if (limit < taskBytes.length+2*LS_LENGTH) {
ByteBuffer big = ByteBuffer.allocate(taskLen);
big.put(taskBytes).put(LINE_SEPARATOR_BYTES);
buffer2file(big, index++);
} else
bb.put(taskBytes).put(LINE_SEPARATOR_BYTES);
}
buffer2file(bb, index++);
}

/** Write a numbered client file if <code>bb не пустые. */
частный недействительными buffer2file(ByteBuffer ББ инт я) {
ББ.флип();
если (ББ.hasRemaining()) {
Строке filename = "tasks_info_" + параметр ClientID + "_" + я + ".тхт";
попробовать (Ява.НИО.каналы.FileChannel
ФК = Ява.НИО.каналы.FileChannel.открыть(
Ява.НИО.файл.Пути.вам(именем, NO_STRINGS),
Ява.НИО.файл.StandardOpenOption.Создание,
Ява.НИО.файл.StandardOpenOption.Пишите)) {
ФК.писать(ВВ);
} поймать (исключение IOException экс) {
экс.печатные();
}
}
ББ.флип();
}
статические заключительные строки NO_STRINGS[] = {},
LINE_SEPARATOR = система.метод getproperty("линии.разделитель");
статические заключительные байт[]
LINE_SEPARATOR_BYTES = LINE_SEPARATOR.метод getbytes();
статические заключительные инт LS_LENGTH = LINE_SEPARATOR_BYTES.длина;

1
ответ дан 11 февраля 2018 в 06:02 Источник Поделиться