2015-09-28 3 views
2

У меня есть текстовый файл объемом 60 МБ, через который моя программа ищет определенный идентификатор и извлекает некоторый связанный текст. И я должен повторить этот процесс для более 200 идентификаторов. Вначале я использовал цикл, чтобы циклически перебирать строки файла и искать идентификатор, а затем извлекать связанный текст, но он занимает слишком много времени (~ 2 мин). Поэтому вместо этого теперь я ищу способ загрузить весь файл в память, затем отыскивать идентификаторы и связанный с ними текст; Я предполагаю, что это должно быть быстрее, чем доступ к жесткому диску более чем в два раза. Поэтому я написал следующий код для загрузки файла в память:Загрузка файла в память (Java)?

public String createLocalFile(String path) 
{ 
    String text = ""; 
    try 
    { 
     FileReader fileReader = new FileReader(path); 
     BufferedReader reader = new BufferedReader(fileReader); 
     String currentLine = ""; 
     while((currentLine = reader.readLine()) != null) 
     { 
      text += currentLine; 
      System.out.println(currentLine); 
     } 

    } 
    catch(IOException ex) 
    { 
     System.out.println(ex.getMessage()); 
    } 
    return text; 
} 

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

Edit: Вот ссылка на файл https://github.com/MVZSEQ/denovoTranscriptomeMarkerDevelopment/blob/master/Homo_sapiens.GRCh38.pep.all.fa

Типичная линия выглядит как:

>ENSP00000471873 pep:putative chromosome:GRCh38:19:49496434:49499689:1 gene:ENSG00000142534 transcript:ENST00000594493 gene_biotype:protein_coding transcript_biotype:protein_coding\ 
MKMQRTIVIRRDYLHYIRKYNRFEKRHKNMSVHLSPCFRDVQIGDIVTVGECRPLSKTVR\ 
FNVLKVTKAAGTKKQFQKF\ 

Где ENSP00000471873 это идентификатор и текст, который я бы извлекая является

MKMQRTIVIRRDYLHYIRKYNRFEKRHKNMSVHLSPCFRDVQIGDIVTVGECRPLSKTVR\ 
    FNVLKVTKAAGTKKQFQKF\ 
+1

Вы не получаете доступ к жесткому диску 200 раз. Никакая нормальная операционная система не работает таким образом. Поместите файл в какую-то разумную структуру, например, в массив строк. –

+0

Если вы пытаетесь сохранить какую-то «базу данных» в текстовом файле, возможно, вам следует использовать DATABASE –

+4

. Вместо StringButler вы можете использовать 'StringBuilder' (возможно, компилятор уже конвертирует ваш код для его использования). –

ответ

1

Если файл содержит коллекцию записей, то вы можете
1. Создайте класс с атрибутами идентификатора и текста.
2. Запишите каждую запись из файла и создайте из нее объект и добавьте его в HashMap.
3. Используйте HashMap для извлечения объектов по ID

+0

К сожалению, это не организовано так. –

1

Согласен с большинством других комментариев. 60 МБ не слишком велико для сегодняшних воспоминаний. Но там, где время засасывается, почти наверняка присутствует в том, что «+ =» добавляет каждую строку к все более чудовищной одиночной строке. Создайте массив строк.

Еще лучше, выделите текст ID и «связанный текст» во время чтения, чтобы ускорить поиск более позднего идентификатора. Хэш-таблица была бы идеальной.

+0

Это точно, + = * - плохая идея. Тем не менее, подход должен быть изменен более, так что данные получают некоторую структуру, а не просто хранят весь файл в памяти как необработанные байты. Поэтому я считаю, что этот ответ на самом деле не помогает наилучшим образом. –

1

Вы, безусловно, находитесь на правильном пути, думая, что вы должны прочитать это в памяти и получить доступ к нему через какое-то сопоставление. Это позволит устранить узкое место, а именно - дисковый ввод-вывод и время доступа (память намного быстрее).

Я бы рекомендовал прочитать данные в HashMap с идентификатором, являющимся ключом, а текст - значением.

Попробуйте что-то вроде:

public Map<Integer, String> getIdMap(final String pathToFile) throws IOException { 
    // we'll use this later to store our mappings 
    final Map<Integer, String> map = new HashMap<Integer, String>(); 
    // read the file into a String 
    final String rawFileContents = new String(Files.readAllBytes(Paths.get(pathToFile))); 
    // assumes each line is an ID + value 
    final String[] fileLines = rawFileContents.split(System.getProperty("line.separator")); 
    // iterate over every line, and create a mapping for the ID to Value 
    for (final String line : fileLines) { 
     Integer id = null; 
     try { 
      // assumes the id is part 1 of a 2 part line in CSV "," format 
      id = Integer.parseInt(line.split(",")[0]); 
     } catch (NumberFormatException e) { 
      e.printStackTrace(); 
     } 
     // assumes the value is part 2 of a 2 part line in CSV "," format 
     final String value = line.split(",")[1]; 
     // put the pair into our map 
     map.put(id, value); 
    } 
    return map; 
} 

Это будет читать файл в память (в виде строки), а затем вырезать его в Map так, что это легко получить значения, например:

Map<Integer, String> map = getIdMap("/path/to/file"); 
final String theText = map.get(theId); 
System.out.println(theText); 

Этот образец кода не проверен и содержит некоторые предположения о вашем формате файла, а именно, что это один идентификатор и значение для каждой строки, а также то, что идентификаторы и значения разделены запятыми (CSV). Конечно, если ваши данные структурированы несколько иначе, просто настройтесь на вкус.

ОБНОВЛЕНО, чтобы соответствовать вашему описанию файла:

public Map<String, String> getIdMap(final String pathToFile) throws IOException { 
    // we'll use this later to store our mappings 
    final Map<String, String> map = new HashMap<String, String>(); 
    // read the file into a String 
    final String rawFileContents = new String(Files.readAllBytes(Paths.get(pathToFile))); 
    // assumes each line is an ID + value 
    final String[] fileLines = rawFileContents.split(System.getProperty("line.separator")); 
    // iterate over every line, and create a mapping for the ID to Value 
    for (final String line : fileLines) { 
     // get the id and remove the leading '>' symbol 
     final String id = line.split(" ")[0].replace(">", "").trim(); 
     // use the key 'transcript_biotype:' to get the 'IG_D_gene' value 
     final String value = line.split("transcript_biotype:")[1].trim(); 
     // put the pair into our map 
     map.put(id, value); 
    } 
    return map; 
} 
0

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

public byte[] loadFile(File f) throws IOException { 
    long size = f.length(); 
    InputStream source; 
    byte[] bytes; 
    int nread; 
    int next; 

    if (size > Integer.MAX_VALUE) { 
     throw new IllegalArgumentException("file to long"); 
    } 
    bytes = new byte[(int)size]; 

    source = new FileInputStream(f); 

    for (next = 0; next < bytes.length; next += nread) { 
     nread = source.read(bytes, next, bytes.length - next); 
     if (nread < 0) { 
      throw new FileTruncatedWhileReadingItException(); 
      // or whatever ... 
     } 
    } 
    if (source.read() != -1) { 
     throw new FileExtendedWhileReadingItException(); 
     // or whatever ... 
    } 

    return bytes; 
} 

Вы можете затем обработайте эту копию в памяти вместо чтения с диска, создав вокруг нее ByteArrayInputStream - вы должны с легкостью подключить это к существующему коду.

Могут быть другие способы оптимизации еще больше. Например, если обработка данных обязательно включает в себя декодирование их на символы, то вы можете кэшировать результаты декодирования с помощью Reader для чтения в char[] вместо InputStream для чтения в byte[], а затем, аналогичным образом. Обратите внимание, однако, что сохранение данных ASCII в форме char занимает в два раза больше места, чем сохранение его в форме byte.

Если данные подходят, то, вероятно, было бы полезно выполнить полный анализ в более сложную структуру данных, такую ​​как Map, что могло бы сделать последующие поиски чрезвычайно быстрыми. Цена, конечно, еще больше использования памяти.

+0

MappedByteBuffer? – ZhongYu

+0

@ bayou.io, 'MappedByteBuffer', безусловно, является альтернативой. Он имеет разные преимущества и недостатки. Скорее всего, это будет намного быстрее, но это частично связано с тем, что загрузка данных из файла в память может быть амортизирована после последующих обращений. Неясно, следует ли ожидать, что общее время доступа к данным будет улучшено. Кроме того, сопоставление памяти с файлом оставляет результат чувствительным к изменениям в базовом файле, что может быть или не быть желательным. Если вам не нужно будет * изменять * данные, то я склонен предпочитать их загрузку. –

0

Я думаю, что ваша проблема связана с добавлением строки на текст. Вместо этого вы должны использовать StringBuffer. Я также советую вам использовать Scanner класс вместо FileReader:

public String createLocalFile(String path) 
{ 
    StringBuffer text = new StringBuffer(); 
    try 
    { 
     Scanner sc = new Scanner(new File(path)); 
     while(sc.hasNext()) 
     { 
      String currentLine = sc.nextLine(); 
      text.append(currentLine); 
      System.out.println(currentLine); 
     } 

    } 
    catch(IOException ex) 
    { 
     System.out.println(ex.getMessage()); 
    } 
    return text.toString(); 
} 

Это должно быть намного быстрее.

+0

Здесь нет необходимости использовать 'StringBuffer', если OP не нуждается в безопасности потоков (и связанных с ним служебных данных и' StringBuffer'). Вместо этого, 'StringBuilder', вероятно, будет здесь просто отлично. – SnakeDoc

0

С чем вы работаете, является файлом FASTA. Дайте BioPerl попытку ... есть тонны библиотек для разбора и работы с этими файлами. Что бы вы ни делали, это, скорее всего, уже сделано ...

Смежные вопросы