2

我找到了几种方法来计算 Java 文件中单个字符的出现次数。我的问题很简单:有没有办法同时计算文件中列表中任何字符的出现,还是我必须遍历每个字符?

为了澄清,我想要的东西相当于:对于文件中的每个字符,如果列表“abcdefg”中的字符增加 1。

背景:我正在计算文件中的谓词,我能想到的最好方法是搜索 <、>、== 等的出现。

4

6 回答 6

4

使用 aMap<Character, Integer>并浏览文件。对于您测试的每个角色,看看它是否在地图中。如果不是加上值1,否则获取当前值,将其递增并放回。测试两者TreeMapHashMap看看哪个最适合你。现在您有了一个完整的直方图,您可以轻松添加有趣的总和。

更新:看到您对查找序列感兴趣。如果你想以良好的性能做到这一点,我会使用像 lex 这样的工具,但用于 Java。一个快速的谷歌把我带到了这个:http ://www.cs.princeton.edu/~appel/modern/java/JLex/应该很直接地定义你感兴趣的令牌,然后它应该很计算它们很简单。

更新 2:我忍不住想玩它。这是一个使用上述工具似乎可以工作的示例(免责声明:我没有使用该工具,所以这可能是完全错误的......):

import java.lang.System;
import java.util.Map;
import java.util.TreeMap;

class Sample {
  public static void main(String argv[]) throws java.io.IOException {
    Map<String,Integer> map = new TreeMap<>();

    Yylex yy = new Yylex(System.in);
    Yytoken t;
    while ((t = yy.yylex()) != null) {
      String text = t.mText;

      if (!text.isEmpty()) {
        Integer i = map.get(text);
        if (i == null) {
          map.put(text, 1);
        }
        else {
          map.put(text, map.get(text)+1);
        }
      }
    } 

    System.out.println(map);
  }
}

class Yytoken {
  public String mText;

  Yytoken(String text) {
   mText = text;
  }

  public String toString() {
    return "Token: " + mText;
  }
}

%%

OTHER=(.|[\r\n])

%% 

<YYINITIAL> "," { return (new Yytoken(yytext())); }
<YYINITIAL> ":" { return (new Yytoken(yytext())); }
<YYINITIAL> ";" { return (new Yytoken(yytext())); }
<YYINITIAL> "(" { return (new Yytoken(yytext())); }
<YYINITIAL> ")" { return (new Yytoken(yytext())); }
<YYINITIAL> "[" { return (new Yytoken(yytext())); }
<YYINITIAL> "]" { return (new Yytoken(yytext())); }
<YYINITIAL> "{" { return (new Yytoken(yytext())); }
<YYINITIAL> "}" { return (new Yytoken(yytext())); }
<YYINITIAL> "." { return (new Yytoken(yytext())); }
<YYINITIAL> "+" { return (new Yytoken(yytext())); }
<YYINITIAL> "-" { return (new Yytoken(yytext())); }
<YYINITIAL> "*" { return (new Yytoken(yytext())); }
<YYINITIAL> "/" { return (new Yytoken(yytext())); }
<YYINITIAL> "=" { return (new Yytoken(yytext())); }
<YYINITIAL> "<>" { return (new Yytoken(yytext())); }
<YYINITIAL> "<"  { return (new Yytoken(yytext())); }
<YYINITIAL> "<=" { return (new Yytoken(yytext())); }
<YYINITIAL> ">"  { return (new Yytoken(yytext())); }
<YYINITIAL> ">=" { return (new Yytoken(yytext())); }
<YYINITIAL> "&"  { return (new Yytoken(yytext())); }
<YYINITIAL> "|"  { return (new Yytoken(yytext())); }
<YYINITIAL> ":=" { return (new Yytoken(yytext())); }
<YYINITIAL> "#" { return (new Yytoken(yytext())); }
<YYINITIAL> {OTHER} { return (new Yytoken("")); }
于 2012-11-08T19:28:22.727 回答
2
  • 阅读

由于您要计算超过 1 个字符(==、!=、<-、>=)的谓词,因此您需要一个PushBackReader以便您可以查看下一个字符以确定实际谓词。

  • 发生频率

如果你能负担得起额外的依赖,那么我的建议是使用Multiset来计算频率。如果你不能,那么你可以使用基于 Map 或数组的计数器(如果你的谓词集是有限的,我更喜欢这个,因为这简化了代码)。

  • 并行化?

使用上述方法更简单,因为您可以通过 1 次单程获得频率。如果您的文件很大或者必须计算许多文件的频率,那么您可以选择使用 java Executors 并行化它。

于 2012-11-08T19:49:43.500 回答
2

存储

如果我理解正确,您不仅希望找到单个字符的出现次数,还希望找到短字符序列(即字符串)的出现次数,例如==. 在这种情况下, aMap<Character, Integer>是不够的,您需要 aMap<String, Integer>为每个字符串存储一个计数。

您也可以使用GuavaMultiset,这对于一个知道它包含多少次重复(相同)元素的集合来说基本上是一个很好的接口。

我相信定义了您想要计算的谓词/运算符/任何短字符串的数量,您可以定义一个数组/一个列表来存储您感兴趣的所有谓词,例如:

List<String> operators = Arrays.asList("==", "<=", ">=", "<", ">");

然后,您将“倾倒”所有这些运算符作为映射的键并将它们的值初始化为零:

Map<String, Integer> counts = new HashMap<>();
for (String operator : operators)
    counts.put(operator, 0);

解析

至于解析,您可以使用Scanner轻松逐行读取文件。对于每一行,您可以使用这样的方法来计算它包含给定子字符串的次数:

static int occurrences(String source, String subString) {
    int count = 0;
    int index = source.indexOf(subString);

    while (index != -1) {
        count++;
        index = source.indexOf(subString, index + 1);
    }
    return count;
}

然后以与此类似的方式使用此方法:

Scanner scanner = new Scanner(new File("input.txt"));
while (scanner.hasNextLine()) {
    String line = scanner.nextLine();
    for (String operator : operators) {
        int oldOccurences = counts.get(operator);
        counts.put(operator, oldOccurences + occurrences(line, operator));
    }
}
于 2012-11-08T20:01:50.403 回答
1

我相信java列表接口有一个Contains()方法,所以你可以做类似的事情

if(someList.Contains('<'))
{
    x++
}

IT 实际上并没有一次检查所有这些,但无论如何这些东西对你是隐藏的

http://docs.oracle.com/javase/1.4.2/docs/api/java/util/List.html

于 2012-11-08T19:27:54.933 回答
1

对“ count the occurrence of any of the characters in a list in a file simultaneously”:

  • 您可以使用 HashTable,其中键是字符,值是您看到该字符的次数。
  • 每次读取一个字符时,检查它是否在 HashTable 中:
    • 如果是,则将其值增加 1
    • 如果不是,则将键、值对添加到初始化为 1 的 HashTable

如果您关心的字符集很小(例如您的示例中的"abcdefg"or "<, >, =="),则 switch 语句就足够了,而不是使用 HashTable 来解决问题

于 2012-11-08T19:29:15.950 回答
0

一个简单的方法是使用数组:

final int[] occurs = new int[65536];
for (char c : file) occurs[c]++;

如果您知道不会遇到太奇特的字符,则可以减小数组的大小。

于 2012-11-08T19:31:48.850 回答