Лучший способ записать строку в файл, используя Java Nio - PullRequest
37 голосов
/ 09 сентября 2011

Мне нужно написать (добавить) огромную строку в плоский файл с помощью 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 мс

Ответы [ 5 ]

53 голосов
/ 24 февраля 2014

Существует решение в одну строку , использующее 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 должно быть быстрым, а код довольно маленьким.

15 голосов
/ 10 сентября 2011

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

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

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

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

Приветствия,

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

Поскольку @EJP предупредил меня, что прямые буферы не будут эффективны для этой проблемы, я сам проверил ее и получил хороший результат.NIO-решение с использованием неморированных файлов.В моем 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);
8 голосов
/ 10 сентября 2011

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

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

0 голосов
/ 08 сентября 2016

Вот короткий и простой способ.Он создает файл и записывает данные, относящиеся к вашему проекту кода:

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();
    }
}
0 голосов
/ 13 июня 2014

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

//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();
...