簡體   English   中英

查找,然后有效地用java中的反向替換

[英]finding, and then efficiently replacing with the reverse in java

我正在使用Java處理具有很大的蛋白質.txt文件數據庫。 這些蛋白質具有一般的結構,但沒有一個足夠統一的結構來硬編碼“將其從startIndex轉移到endIndex,反向並替換”。 唯一真正的統一性是它們之間用>分隔,例如:

...WERINWETI>gi|230498 [Bovine Albumin]ADFIJWOENAONFOAIDNFKLSADNFATHISDATFDAIFJ>sp|234235 (human) AGP1 QWIQWONOQWNROIWQRNOQWIRNSWELLE>gi|...等。

如您所見,盡管實際蛋白質序列(所有大寫字母的長鏈)是統一的,因為它們是大寫字母的鏈,但是除此之外,前面的描述幾乎可以是任何東西(很多時候沒有空格)在說明和順序之間)。 我的程序需要做的是將原始文本復制到一個新文件中,然后遍歷,在每個>之后添加一個r- (例如...EERFDS>r-gi|23423... ),然后僅反向資本鏈。 該過程完成后,我需要將其附加到原始文本的末尾。

我已經完成了r-函數,實際上我也已經完成了逆轉和追加操作,但是效率不夠。 正在接受這種處理的數據庫是MASSIVE,我的程序花了太長時間。 實際上,我不知道要花多長時間,因為我從未讓它完成。 我等了1個小時就結束了。 這是我使用正則表達式(內置Pattern類)(計算密集型部分)進行逆運算的算法:

Pattern regexSplit = Pattern.compile(">");
String[] splits = regexSplit.split(rDash.toString());
StringBuilder rDashEdited = new StringBuilder();
Pattern regexProtein = Pattern.compile("[A-Z]{5,}");

for (int splitIndex = 1; splitIndex < splits.length; splitIndex++) {
    Matcher rDashMatcher = regexProtein.matcher(splits[splitIndex]);
    rDashMatcher.find();
    StringBuffer reverser = new StringBuffer(rDashMatcher.group());
    rDashEdited.append(rDashMatcher.replaceAll(reverser.reverse().toString()) + ">");
}
System.out.println(">" + rDashEdited);

因此,基本上,我將rDash (這是一個StringBuilder,其中包含所有已插入>r-所有原始蛋白質,但尚未經過逆轉)分解為每個蛋白質,並將它們添加到String數組中。 然后,我遍歷數組中的每個字符串,尋找長度超過5個字母的大寫字母鏈,將匹配項添加到StringBuffer中,將其反轉,然后將反向版本替換為反向。 請注意,此算法按預期適用於較小的文本文件。

是否有更強大的正則表達式消除了拆分/遍歷數組的需要? 當我嘗試時, replaceAll()調用將所有下游蛋白質替換為集合中FIRST蛋白質的逆序。 我用System.out.println(rDashMatcher.groupCount())進行了有趣的檢查,並為集合中的每種蛋白質打印了0 誰能幫助我使用更高效/更強大的正則表達式? 對我來說,這是一個相當新的概念,但它使我想起了在MATLAB中進行矢量化的過程(僅使用字母)。

我為此扔了10,000,000條記錄(大約379MB文本文件),並花了1:06分鍾。(4core athlon,幾年前)

大的if樹處理只得到一半的末端,因為定界符位於元素的中間。

public void readProteins(BufferedReader br, BufferedWriter bw) throws IOException
{     
  Pattern regexSplit = Pattern.compile(">");
  Pattern proteinPattern = Pattern.compile("(.*?)([A-Z]{5,})");
  Matcher m;
  Scanner s = new Scanner(br);
  s.useDelimiter(regexSplit);         
  while (s.hasNext())
  {
      StringBuffer sb = new StringBuffer();
      String protein = s.next();
      m = proteinPattern.matcher(protein);            
      if (m.find())
          sb.append(m.group(2)).reverse().append(">r-").insert(0, m.group(1));
      else
          sb.append(protein);
      );          
  }
  bw.flush();
  bw.close();
}

一些優化建議:

  • 避免使用StringBuffer。 StringBuilder提供相同的功能並且速度更快。
  • 您可以使用stringBuilder.replace(int start,int end,String str)而不是replaceAll來避免嘗試再次匹配整個字符串中的模式。
  • 使用方法#b,您甚至可以跳過拆分,繼續搜索蛋白質並在出現時替換它們。

總是最好與探查器一起運行,並查看正在消耗時間而不是猜測的時間。 例如,可以通過增加程序的內存或避免某些慢的文件系統等來提高性能。

您不需要功能更強大的正則表達式,只需簡化流程即可,這樣就不必一遍又一遍地處理相同的文本。 在大多數情況下,這意味着使用Java的較低級別的regex API,即appendReplacement()appendTail() 通過將一個空字符串傳遞給appendReplacement()我避免了它對后向引用的自動處理。

注意我也是如何使用find() 如果你發現自己在調用find() (或matches()lookingAt()沒有檢查它的返回值,你做錯了什么。 這樣您才能知道比賽是否成功。

public static void main(String[] args) throws Exception
{
  // this I/O code is bare-bones so as not to distract from the fun stuff
  BufferedWriter bw = new BufferedWriter(new FileWriter("test_out.txt"));

  // I use a lookahead so the ">" doesn't get discarded
  Scanner sc = new Scanner(new File("test.txt")).useDelimiter("(?=>)");
  while (sc.hasNext())
  {
    bw.write(reverseCapBlocks(sc.next()));
  }
  sc.close();
  bw.close();
}

// cache these because recompiling them is fairly expensive
static final Pattern CAPS_PATTERN = Pattern.compile("\\b[A-Z]{5,}\\b");
static final Pattern BRACKET_PATTERN = Pattern.compile("^>");

static String reverseCapBlocks(String s)
{
  StringBuffer sb = new StringBuffer();
  Matcher m = CAPS_PATTERN.matcher(s);
  while (m.find())
  {
    // appends whatever was between the last match and this one
    // but hole off on appending the current match
    m.appendReplacement(sb, "");
    String temp = m.group();

    // do the reversing manually because it's trivial and it avoids
    // creating a new StringBuilder every time
    for (int i = temp.length() - 1; i >= 0; i--)
    {
      sb.append(temp.charAt(i));
    }
  }
  // append whatever was left after the last match
  m.appendTail(sb);

  // if the chunk began with ">", add the "r-"
  return BRACKET_PATTERN.matcher(sb).replaceFirst(">r-");
}

我使用StringBuffer而不是StringBuilder,因為這是API所要求的,但這並不重要。 關於StringBuffer的低效率的報告雖然是正確的,但往往會被誇大。

正如我在評論中提到的那樣,您不應將整個文件加載到內存中。 這將導致內存換入和換出,並使程序變慢。

如果“蛋白質”的大小(即>定界的字符串)在內存中是可管理的,則應采取措施

    Scanner scanner = null;
    BufferedWriter writer = null;
    try {
        writer = new BufferedWriter(new FileWriter("output.txt"));
        scanner  = new Scanner(new BufferedReader(new FileReader("input.txt")));
        scanner.useDelimiter(">");
        while ( scanner.hasNext() ) {
           doReverseAndWriteToFile(scanner.next(), writer);
        }
    } finally {
        if ( scanner != null) {
            scanner.close();
        }
        if ( writer != null ) {
            writer.flush();
            writer.close();
        }
    }

doReverseAndWriteToFile() ,應將程序的第二部分放在其中(我不太注意:-))。 在執行此功能時,還應繼續進行操作以寫入新文件。

如果使用此功能,一次只能在內存中存儲“ bufferSize” +“一種蛋白質的長度”。

看看是否可以加快速度。.否則,您必須尋找其他地方。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM