Подтвердить что ты не робот

Лучший способ записи String в файл с помощью java nio

Мне нужно написать (добавить) огромную строку в плоский файл с помощью Java Java. Кодировка ISO-8859-1.

В настоящее время мы пишем, как показано ниже. Есть ли лучший способ сделать то же самое?

public void writeToFile(Long limit) throws IOException{
     String fileName = "/xyz/test.txt";
     File file = new File(fileName);        
     FileOutputStream fileOutputStream = new FileOutputStream(file, true);  
     FileChannel fileChannel = fileOutputStream.getChannel();
     ByteBuffer byteBuffer = null;
     String messageToWrite = null;
     for(int i=1; i<limit; i++){
         //messageToWrite = get String Data From database
         byteBuffer = ByteBuffer.wrap(messageToWrite.getBytes(Charset.forName("ISO-8859-1")));
         fileChannel.write(byteBuffer);         
     }
     fileChannel.close();
}

РЕДАКТИРОВАТЬ: пробовал оба варианта. Ниже приведены результаты.

@Test
public void testWritingStringToFile() {
    DiagnosticLogControlManagerImpl diagnosticLogControlManagerImpl = new DiagnosticLogControlManagerImpl();
    try {
        File file = diagnosticLogControlManagerImpl.createFile();
        long startTime = System.currentTimeMillis();
        writeToFileNIOWay(file);
        //writeToFileIOWay(file);
        long endTime = System.currentTimeMillis();
        System.out.println("Total Time is  " + (endTime - startTime));
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

/**
 *
 * @param limit
 *            Long
 * @throws IOException
 *             IOException
 */
public void writeToFileNIOWay(File file) throws IOException {
    FileOutputStream fileOutputStream = new FileOutputStream(file, true);
    FileChannel fileChannel = fileOutputStream.getChannel();
    ByteBuffer byteBuffer = null;
    String messageToWrite = null;
    for (int i = 1; i < 1000000; i++) {
        messageToWrite = "This is a test üüüüüüööööö";
        byteBuffer = ByteBuffer.wrap(messageToWrite.getBytes(Charset
            .forName("ISO-8859-1")));
        fileChannel.write(byteBuffer);
    }
}

/**
 *
 * @param limit
 *            Long
 * @throws IOException
 *             IOException
 */
public void writeToFileIOWay(File file) throws IOException {
    FileOutputStream fileOutputStream = new FileOutputStream(file, true);
    BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(
        fileOutputStream, 128 * 100);
    String messageToWrite = null;
    for (int i = 1; i < 1000000; i++) {
        messageToWrite = "This is a test üüüüüüööööö";
        bufferedOutputStream.write(messageToWrite.getBytes(Charset
            .forName("ISO-8859-1")));
    }
    bufferedOutputStream.flush();
    fileOutputStream.close();
}

private File createFile() throws IOException {
    File file = new File(FILE_PATH + "test_sixth_one.txt");
    file.createNewFile();
    return file;
}

Использование ByteBuffer и Channel: заняло 4402 мс

Использование буферизованного устройства записи: заняло 563 мс

4b9b3361

Ответ 1

Я не думаю, что вы сможете получить строгий ответ без тестирования своего программного обеспечения. NIO может значительно ускорить работу приложения в правильных условиях, но также может замедлить работу. Вот несколько моментов:

  • Тебе действительно нужны струны? Если вы храните и получаете байты из своей базы данных, вы можете избежать затрат на размещение строк и кодирование.
  • Вам действительно нужно rewind и flip? Похоже, вы создаете новый буфер для каждой строки и просто записываете его в канал. (Если вы пойдете по пути NIO, оцените стратегии, которые используют буферы вместо того, чтобы оборачивать/отбрасывать, я думаю, что они будут лучше).
  • Имейте в виду, что wrap и allocateDirect могут создавать совершенно разные буферы. Оцените оба, чтобы понять компромиссы. При прямом выделении обязательно используйте один и тот же буфер для достижения максимальной производительности.
  • И самое главное: обязательно сравните NIO с подходами BufferedOutputStream и/или BufferedWritter (также используйте промежуточный буфер byte[] или char[] с приемлемым размером). Я видел много- много- много людей, которые обнаружили, что NIO - это не серебряная пуля.

Если вам нравится какой-то кровавый край... Вернемся к IO Trails для некоторого NIO2: D.

И вот интересный тест на копирование файлов с использованием различных стратегий. Я знаю, что это другая проблема, но я думаю, что большинство фактов и авторских заключений также применимы к вашей проблеме.

Ура,

ОБНОВЛЕНИЕ 1:

Поскольку @EJP предупредил меня, что прямые буферы не будут эффективны для этой проблемы, я сам проверил ее и получил хорошее решение NIO с использованием файлов, отображаемых с помощью nemory. В моем Macbook под управлением OS X Lion это значительно превосходит BufferedOutputStream. но имейте в виду, что это может зависеть от ОС/оборудования/ВМ:

public void writeToFileNIOWay2(File file) throws IOException {
    final int numberOfIterations = 1000000;
    final String messageToWrite = "This is a test üüüüüüööööö";
    final byte[] messageBytes = messageToWrite.
            getBytes(Charset.forName("ISO-8859-1"));
    final long appendSize = numberOfIterations * messageBytes.length;
    final RandomAccessFile raf = new RandomAccessFile(file, "rw");
    raf.seek(raf.length());
    final FileChannel fc = raf.getChannel();
    final MappedByteBuffer mbf = fc.map(FileChannel.MapMode.READ_WRITE, fc.
            position(), appendSize);
    fc.close();
    for (int i = 1; i < numberOfIterations; i++) {
        mbf.put(messageBytes);
    }
} 

Я признаю, что немного обманул, рассчитав общий размер для добавления (около 26 МБ) заранее. Это может быть невозможно для нескольких реальных сценариев. Тем не менее, вы всегда можете использовать "достаточно большой добавочный размер для операций, а затем обрезать файл.

ОБНОВЛЕНИЕ 2 (2019):

Любой, кто ищет современное (например, Java 11+) решение этой проблемы, я бы следовал совету @DodgyCodeException и использовал java.nio.file.Files.writeString:

String fileName = "/xyz/test.txt";
String messageToWrite = "My long string";
Files.writeString(Paths.get(fileName), messageToWrite, StandardCharsets.ISO_8859_1);

Ответ 2

Существует однострочное решение, использующее Java nio:

java.nio.file.Files.write(Paths.get(file.toURI()), "My string to save".getBytes("utf-8"), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);

Я не сравнивал это решение с другими, но использование встроенной реализации для файла open-write-close должно быть быстрым, а код довольно мал.

Ответ 3

BufferedWriter вокруг FileWriter почти наверняка будет быстрее, чем любая схема NIO, с которой вы можете столкнуться. Ваш код, конечно, не оптимален, с новым ByteBuffer для каждой записи, а затем делает бессмысленные операции над ним, когда он собирается выйти из сферы действия, но в любом случае ваш вопрос основан на неправильном представлении. NIO вообще не "выгружает область памяти в ОС", если вы не используете FileChannel.transferTo/From(), чего вы не можете в этом случае.

NB не используют PrintWriter, как это предлагается в комментариях, поскольку это проглатывает исключения. PW действительно только для консолей и файлов журналов, где вам все равно.

Ответ 4

Вот короткий и простой способ. Он создает файл и записывает данные относительно вашего проекта кода:

private void writeToFile(String filename, String data) {
    Path p = Paths.get(".", filename);
    try (OutputStream os = new BufferedOutputStream(
        Files.newOutputStream(p, StandardOpenOption.CREATE, StandardOpenOption.APPEND))) {
        os.write(data.getBytes(), 0, data.length());
    } catch (IOException e) {
        e.printStackTrace();
    }
}

Ответ 5

Это работает для меня:

//Creating newBufferedWritter for writing to file
BufferedWritter napiš = Files.newBufferedWriter(Paths.get(filePath));
                    napiš.write(what);
//Don't forget for this (flush all what you write to String write):                     
                    napiš.flush();