0

混合データの文字列、いくつかの単語と数字があります。数値は、整数全体、整数の比率、または整数全体の前のパーセント記号のいずれかです。プログラムの実行中 (データベースではなく)、この情報を Map (それが理にかなっている場合は別のタイプのオブジェクトである可能性があります) に保存しようとしています。パーセント記号を除いて、残りのデータは問題なく解析されます。データがコロン付きの変数のこの正確な形式であると常に期待できます。

正しい出力 (タブは面白いインデントを与えます):

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

1Map<String,String>枚目は 、2枚目は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を改善できますか?ratiosmapStringToRatio

4

2 に答える 2

1

データをどのような種類のデータ構造に格納するかを決定するためには、データをどうするかが非常に重要です。印刷するだけなら、保管するのは時間の無駄です。しかし、このデータを印刷しているだけではないはずですよね?

キーが繰り返されない限り、マップは問題ありません。そうしないと、既存の値を同じキーを持つ新しい値に置き換えることになります。それが問題だと思わない場合は、マップを保持できます。

もう 1 つの考えられる解決策は、キーを Ratio 自体の内部に格納することです。したがって、比率オブジェクトには「名前」メンバーがあり、データを比率のリストに保存できます。

あなたの Ratio オブジェクトが気に入りました。これ以上追加 (または削除) することはないと思います。Regexp は複雑で読みにくく、コードが何をしているのかを理解するのが難しいことに同意します。しかし、あなたが与えた解決策は素晴らしくてきれいだとも思います。コードをよりシンプルで読みやすくするために、名前付きグループでパターンを使用し、すべてを 1 つのパターンのみに入れることができます。次のコードを書きました。

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()

私ができることの 1 つは、このロジックをテストしやすい別のクラスに入れることです。すべてを静的変数としてメイン メソッドから実行することはお勧めしません。

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 チェーンになってしまうことです。また、多くの異なるブランチが含まれるため、テストも難しくなります。新しい値の型を追加する予定がない場合は、それで問題ありません。

これを大幅に拡張する予定がある場合は、RationProcessor インターフェイスと、PercentageRatioProcessor や DivisionRatioProcessor などのさまざまな実装を作成する、より抽象的なアプローチを使用します。このインターフェイスには、ブール値と比率をそれぞれ返す「canProcess」メソッドと「process」メソッドがあります。それが使用する正しいプロセッサであり、オブジェクトが処理された Ratio であるかどうかを示すブール値。

于 2013-12-18T16:52:07.890 に答える
0

これは機能します。それが正しいかどうかはよくわかりませんが、それほど悪くはないと思います。

結果:

thufir@dur:~/NetBeansProjects/StackOverflow$ 
thufir@dur:~/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
thufir@dur:~/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);
        }
    }
}
于 2013-12-18T21:06:16.837 に答える