2013-12-18 39 views
0

我有一串混合数据,一些单词和数字。数字可以是整数,整数的比率,也可以是整数前的百分号。我试图在程序运行期间(而不是数据库)将这些信息存储在Map中(可能是另一种类型的对象,如果有意义的话)。撇开百分号,剩下的数据被解析好了。我总是可以期待这些数据是以冒号形式存在的。如何将Map <String,String>更改为Map <String,Ratio>,比率为x/y

正确的输出(制表符给出滑稽缩进):

AB: 272/272 CD: 204/529 EFGH: 105 HIJKL: 105 MN: 0 OPQ: 0% 
AB  272/272 
HIJKL  105 
CD  204/529 
MN  0 
EFGH  105 
OPQ  0% 
----------- 
AB  272/272 
CD  204/529 
HIJKL  105/1 
MN  0/1 
EFGH  105/1 
OPQ  0/1 

第一打印与Map<String,String>,第二个是用Map<String,Ratio>。如果有比我自制比例更好的选择,我会很乐意使用它。

笨拙的代码,是的,过度使用静态的,只是为了容易复制/粘贴:

package regex; 

import java.util.HashMap; 
import java.util.Map; 
import java.util.regex.Matcher; 
import java.util.regex.Pattern; 
import static java.lang.System.out; 

class Ratio { 

    private int numerator; 
    private int denominator; 

    private Ratio() { 
    } 

    public Ratio(int numerator, int denominator) { 
     this.numerator = numerator; 
     this.denominator = denominator; 
    } 

    public int getNumerator() { 
     return numerator; 
    } 

    public int getDenominator() { 
     return denominator; 
    } 

    public String toString() { 


     return numerator + "/" + denominator; 
    } 
} 

public class Ratios { 

    private static String line = "AB: 272/272 CD: 204/529 EFGH: 105 HIJKL: 105 MN: 0 OPQ: 0%"; 
    private static Map<String, String> rawMapStringToString = new HashMap<>(); 
    private static Map<String, Ratio> mapStringToRatio = new HashMap<>(); 

    public static void main(String[] args) { 
     out.println(line); 
     populateMap(); 
     printMap(rawMapStringToString); 
     out.println("-----------"); 
     ratios(); 
     printMap(mapStringToRatio); 
    } 

    private static void populateMap() { 
     Pattern pattern = Pattern.compile("(\\w+): +(\\S+)"); 
     Matcher matcher = pattern.matcher(line); 
     while (matcher.find()) { 
      rawMapStringToString.put(matcher.group(1), matcher.group(2)); 
     } 
    } 

    private static void printMap(Map<?, ?> m) { 
     for (Map.Entry<?, ?> e : m.entrySet()) { 
      String key = e.getKey().toString(); 
      String val = e.getValue().toString(); 
      out.println(key + "\t\t" + val); 
     } 
    } 

    private static void ratios() { 
     Pattern pattern = Pattern.compile("(\\d+)/(\\d+)"); 
     Pattern p2 = Pattern.compile("(\\w+)"); 
     Matcher m2; 
     int num, den; 
     Ratio ratio = null; 
     for (Map.Entry<String, String> e : rawMapStringToString.entrySet()) { 
      ratio = null; 
      num = 0; 
      den = 1; 
      Matcher matcher = pattern.matcher(e.getValue()); 
      while (matcher.find()) { 
       num = Integer.parseInt(matcher.group(1)); 
       den = Integer.parseInt(matcher.group(2)); 
       ratio = new Ratio(num, den); 
      } 
      if (ratio == null) { 
       m2 = p2.matcher(e.getValue()); 
       while (m2.find()) { 
        num = Integer.parseInt(m2.group()); 
        den = 1; 
        ratio = new Ratio(num, den); 
       } 
      } 
      mapStringToRatio.put(e.getKey(), ratio); 
     } 
    } 
} 

我只是在寻找一种存储这些数据的好办法。当然,百分比可以表示为x/y的比率,只需将分母改为100即可。暂时搁置一下,Map是一个不错的选择?

ratios方法和整体正则表达式似乎很脆弱,尴尬和困难(对我来说),但我不确定如何改进代码。保持Ratio类几乎不变,我该如何改进ratios方法,该方法填充了mapStringToRatio

回答

1

你要做什么与数据是非常重要的,以帮助决定什么样的数据结构来存储它。如果你只是印刷它们,存放它们将会浪费时间。但我很肯定你不只是把这些数据打印出来了吗?

只要您的钥匙不重复,地图就没有问题。否则,您将用具有相同密钥的新密钥替换现有值。如果你认为这不是问题,那么你可以保留地图。

另一个可能的解决方案是将密钥存储在比率本身内。所以你的Ratio对象会有一个“名字”成员,然后你可以将你的数据存储在一个比率列表中。

我喜欢你的Ratio对象,我认为没有太多的东西要添加(或删除)它。我同意Regexp很复杂,很难阅读和理解代码在做什么。但我也认为您提供的解决方案非常干净。为了使代码更简单和更具可读性,您可以使用带有命名组的模式,并将所有内容都放在一个模式中。我写了下面的代码:

Pattern pattern = Pattern.compile("(?<key>\\w+)\\s*:\\s*(?<numerator>\\d+)/*(?<denominator>\\d*)%*"); 
Matcher matcher = pattern.matcher(INPUT); 
while (matcher.find()) { 
    System.out.printf("Key: %s, Numerator: %s, Denominator: %s\n", 
     matcher.group("key"), 
     matcher.group("numerator"), 
     matcher.group("denominator")); 
} 

如果一个组不存在,它将返回一个空字符串。这样,你可以使用的isEmpty测试:

matcher.group("denominator").isEmpty() 

有一件事我会做的是把这种逻辑到一个单独的类,它会更容易测试。不建议使用从主方法运行的静态变量。

如果您正在寻找与Regexp不同的解决方案,您可以使用StringTokenizer来使用空格/制表符分隔它们。然后使用拆分为冒号分割字符串。然后在正确的字符串中检查%或/并以不同的方式处理它们。

喜欢的东西:

StringTokenizer tokenizer = new StringTokenizer(input); 
while (tokenizer.hasMoreTokens()) { 
    String [] nameValuePair = tokenizer.nextToken().split(":"); 
    if (nameValuePair[1].contains("/")) { 
     // process ratio here 
    } else if (nameValurPair[1].contains("%")) { 
     // Process percentage here 
    } else { 
     // Process String here 
    } 
} 

这段代码的缺点是,如果你的值来添加新类型,你会用很长的if/else链告终。测试也很难,因为你会有很多不同的分支。如果你不打算添加新的值类型,那就没问题了。

如果您打算扩大这一点,我会采用更抽象的方法,创建一个RatioProcessor接口以及像PercentageRatioProcessor和DivisionRatioProcessor一样的不同实现。这个接口将有一个“canProcess”方法和一个“process”方法,分别返回布尔值和比率。该布尔值指示是否使用正确的处理器,并且该对象是已处理的比率。

+0

感谢您的反馈。是的,我将使用这些数据,如果不清楚,请原谅。它就像示例代码一样是静态的。我更加关注重复的正则表达式,并认为可能有一种方法可以用一个正则表达式一步完成。但是,也许需要一些难以维护的巨大的,难以理解的(对我来说)正则表达式。这就是'if(ratio == null)'这条线,看起来很尴尬或令人费解。 – Thufir

+0

我添加到我的答案一个解决方案与一个正则表达式使用命名组。这使它更清晰可读!我希望这有助于! – visola

+0

花了我一会儿才弄清楚'matcher = pattern.matcher()'是一种if语句。我在下面添加了我自己的答案。 – Thufir

0

This works。我不太确定这是否正确,但我认为这不是太糟糕。

结果:

[email protected]:~/NetBeansProjects/StackOverflow$ 
[email protected]:~/NetBeansProjects/StackOverflow$ java -jar dist/StackOverflow.jar 
AB  272/272 
CD  204/529 
HIJKL  105/1 
MN  0/1 
EFGH  105/1 
OPQ  0/100 
[email protected]:~/NetBeansProjects/StackOverflow$ 

代码:

package ratios; 

import java.util.HashMap; 
import java.util.Map; 
import java.util.regex.Matcher; 
import java.util.regex.Pattern; 
import static java.lang.System.out; 

public class Ratios { 

    private String input = "AB: 272/272 CD: 204/529 EFGH: 105 HIJKL: 105 MN: 0 OPQ: 0%"; 
    private Map<String, String> strings = new HashMap<>(); 
    private Map<String, Ratio> stringsToRatios = new HashMap<>(); 

    public Ratios() { 
     firstMap(); 
     secondMap(); 
     printMap(stringsToRatios); 
    } 

    public static void main(String[] args) { 
     new Ratios(); 
    } 

    private void secondMap() { 
     Pattern fraction = Pattern.compile("(\\d+)/(\\d+)"); 
     Pattern whole = Pattern.compile("(\\d+)"); 
     Pattern percent = Pattern.compile("(\\d+)%"); 
     Matcher matcher; 
     int num, den; 
     Ratio ratio = null; 
     for (Map.Entry<String, String> e : strings.entrySet()) { 

      matcher = whole.matcher(e.getValue()); 
      while (matcher.find()) { 
       num = Integer.parseInt(matcher.group(1)); 
       den = 1; 
       ratio = new Ratio(num, den); 
      } 

      matcher = fraction.matcher(e.getValue()); 
      while (matcher.find()) { 
       num = Integer.parseInt(matcher.group(1)); 
       den = Integer.parseInt(matcher.group(2)); 
       ratio = new Ratio(num, den); 
      } 


      matcher = percent.matcher(e.getValue()); 
      while (matcher.find()) { 
       num = Integer.parseInt(matcher.group(1)); 
       den = 100; 
       ratio = new Ratio(num, den); 
      } 

      stringsToRatios.put(e.getKey(), ratio); 
     } 
    } 

    private void firstMap() { 
     Pattern pattern = Pattern.compile("(\\w+): +(\\S+)"); 
     Matcher matcher = pattern.matcher(input); 
     while (matcher.find()) { 
      strings.put(matcher.group(1), matcher.group(2)); 
     } 
    } 

    private void printMap(Map<?, ?> m) { 
     for (Map.Entry<?, ?> e : m.entrySet()) { 
      String key = e.getKey().toString(); 
      String val = e.getValue().toString(); 
      out.println(key + "\t\t" + val); 
     } 
    } 
} 
相关问题