首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >查找,然后高效地替换为java中的相反情况。

查找,然后高效地替换为java中的相反情况。
EN

Stack Overflow用户
提问于 2012-06-30 05:05:56
回答 4查看 434关注 0票数 2

我在Java中使用了一个非常大的蛋白质.txt文件数据库。这些蛋白质有一个一般的结构,但没有统一到足以硬编码“将此从startIndex转移到endIndex,反转,并替换”的结构。唯一真正的一致性是它们由>分隔,例如:

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

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

我已经完成了r-函数,实际上我也完成了反转和追加,但效率不够高。接受这种治疗的数据库是巨大的,而我的程序花费的时间太长。事实上,我不知道这需要多长时间,因为我从来没有让它结束。我等了一个小时,然后结束了。下面是我使用regex (内置Pattern类)(计算密集型部分)进行反转的算法:

代码语言:javascript
复制
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()调用用集合中第一个蛋白质的反向替换了所有下游蛋白质。为了好玩,我用System.out.println(rDashMatcher.groupCount())查看了一下,它打印出了集合中每个蛋白质的0。有没有人能帮我做一个更高效/更强大的正则表达式?这对我来说是一个相当新的概念,但它让我想起了MATLAB中的矢量化(只有字母)。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-06-30 08:22:06

我花了1000万条记录(大约379MB的文本文件),花了1:06分钟。(4core athlon,几年前)

大的if树处理你只能得到一半的结尾处,因为分隔符在元素的中间。

代码语言:javascript
复制
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();
}
票数 2
EN

Stack Overflow用户

发布于 2012-06-30 05:37:34

一些优化的想法:

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

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

票数 1
EN

Stack Overflow用户

发布于 2012-06-30 13:29:42

你不需要一个更强大的正则表达式,你只需要简化你的过程,这样你就不会一遍又一遍地处理相同的文本。在大多数情况下,这意味着要使用Java的低级regex API,即appendReplacement()appendTail()。通过向appendReplacement()传递一个空字符串,我避免了它对反向引用的自动处理。

也请注意我是如何使用find()的。如果您曾经发现自己调用find() (或者matches()lookingAt())而没有检查它的返回值,那么您做错了什么。这样你就可以知道匹配是否成功了。

代码语言:javascript
复制
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,因为这是应用程序接口需要的,但这没什么大不了的;有关StringBuffer效率低下的报道虽然是真的,但往往被高度夸大了。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11268946

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档