2

我正在使用非常大的 .txt 蛋白质文件数据库使用 Java。这些蛋白质具有一般结构,但还不够统一,无法硬编码“从 startIndex 到 endIndex,反向和替换”。唯一真正的统一性是它们由 分隔>,例如:

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

正如你所看到的,虽然实际的蛋白质序列(所有大写的长链)是一致的,因为它们是大写的链,但除此之外,前面的描述几乎可以是任何东西(很多时候不是空格在描述和顺序之间)。我的程序需要做的是将原始文本复制到一个新文件中,然后通过,r-在每个之后添加一个>(例如...EERFDS>r-gi|23423...)以及仅反转大写链。该过程完成后,我需要将其附加到原始文本的末尾。

我已经完成了r-功能,实际上我也完成了反转和追加,但是效率不够。接受这种处理的数据库非常庞大,而我的程序花费的时间太长。事实上,我不知道需要多长时间,因为我从不让它完成。我等了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-放入,但还没有经历反转)分成每个单独的蛋白质并将它们添加到一个字符串数组中。然后我遍历数组中的每个字符串并查找长度超过 5 个字母的大写字母链,将匹配项添加到 StringBuffer,反转它,并将正向版本替换为反向版本。请注意,此算法适用于较小的文本文件。

是否有更强大的正则表达式可以消除拆分/遍历数组的需要?当我尝试时,该replaceAll()调用将所有下游蛋白质替换为集合中 FIRST 蛋白质的反面。为了好玩,我检查了一下,System.out.println(rDashMatcher.groupCount())它为集合中的每种蛋白质打印了一个0。任何人都可以帮助我使用更有效/更强大的正则表达式吗?这对我来说是一个相当新的概念,但它让我想起了 MATLAB 中的矢量化(仅使用字母)。

4

4 回答 4

2

我扔了 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();
}
于 2012-06-30T00:22:05.920 回答
1

一些优化思路:

  • 避免使用字符串缓冲区。StringBuilder 提供相同的功能并且速度更快。
  • 您可以使用 stringBuilder.replace(int start, int end, String str) 而不是 replaceAll,这将避免再次尝试匹配整个字符串中的模式。
  • 使用方法#b,您甚至可以跳过拆分继续搜索蛋白质并在出现时替换它们。

最好使用分析器运行,看看是什么在消耗时间而不是猜测。例如,可以通过增加程序的内存或避免某些慢速文件系统等来提高性能。

于 2012-06-29T21:37:33.587 回答
1

您不需要更强大的正则表达式,您只需要简化您的流程,这样您就不会一遍又一遍地处理相同的文本位。在大多数情况下,这意味着使用 Java 的较低级别的正则表达式 API,即appendReplacement()and 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 效率低下的报道虽然属实,但往往被夸大了。

于 2012-06-30T05:29:42.040 回答
0

正如我在评论中提到的,您不应该将整个文件加载到内存中。这将导致内存换入和换出并使您的程序变慢。

如果“蛋白质”的大小(即>分隔字符串)在内存中是可管理的,那么这应该可以解决问题

    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”+“一种蛋白质的长度”在内存中。

看看这是否加快了速度..否则你必须寻找其他地方。

于 2012-06-29T22:46:16.770 回答