ヘ方を解析し桁の数字から文字列をJava
-
11-09-2019 - |
質問
い文字列を含む桁と英数文字を入力してください。い分割の文字列の連続するチャンクの桁の連続するチャンクします。
を考慮する文字列"34A312O5M444123A".
たいと思い出力:["34","A","312","O","5","M","444123","A"]
私がコードすると次のように記述されています。
List<String> digitsAsElements(String str){
StringBuilder digitCollector = new StringBuilder();
List<String> output = new ArrayList<String>();
for (int i = 0; i < str.length(); i++){
char cChar = str.charAt(i);
if (Character.isDigit(cChar))
digitCollector.append(cChar);
else{
output.add(digitCollector.toString());
output.add(""+cChar);
digitCollector = new StringBuilder();
}
}
return output;
}
どのように分割strを取得し配列を含むすべてのチャンク配列を含むすべての文字のチャンク.それに結合させます。私から遠ざかこうに害読む
って意図的に回避を解くことで正規表現パターンを見たいの正規表現パターンの大きな障害を読む
- デバッガな取り扱っています。
- その割り込みの流れを誰かに読むソースコード。
- 時間外regexの成長を有機的になモンスター.
- い非直観的です。
私の質問:
- どのようにしたら良いですか改善の読みやすくするための工夫、上記のコードについて教えてください。
- あると良い。A Utilのクラスは、この課題を解決する優雅.
- いま描きの線を正規表現の分かsimpilarめんどろ風mikiモデルを作ってみ書きしますのでご注意ください。▼
- いを増や可読性/保守性のregExes?
解決
これは1行のコードで問題を解決するものであれば、あなたは正規表現を使用することをいとわないだろうか?
// Split at any position that's either:
// preceded by a digit and followed by a non-digit, or
// preceded by a non-digit and followed by a digit.
String[] parts = str.split("(?<=\\d)(?=\\D)|(?<=\\D)(?=\\d)");
正規表現を説明するためのコメントで、私はそれが(そのことについて、または他の正規表現のソリューションのいずれかの)非正規表現のソリューションのいずれよりもより読みやすいと思います。
他のヒント
この特定のタスクのために私は常に正規表現の代わりに、同様の手書きのものを使用すると思います。あなたは上記の与えているコードは、少なくとも私には、(私の知る限り、このような場合には(\d+|[^\d]+)
されるだろう)単純な正規表現よりも読みにくくている。
あなたはいくつかの行を超える正規表現を書き込まないようにすることができます。それらはなると通常理解するの読めないと難しいです、はなく、そう、彼らはと交換することができるコードがあることができます!のパーサはかなり近いことはありません、あなたが通常にしようとしてよりオリジナルの文法を読んだ方がいいでしょう生成された(または手書き)パーサの感覚。同じことは、正規文法のちょうど簡潔な説明されている正規表現のために(私見)になります。
だから、一般的に私はあなたがひどく愚かなアイデアのように聞こえるあなたの質問に与えてくれたようなコードの賛成で正規表現を禁止すると思います。そして、正規表現は単なるツール、あまり何もない、何よりもです。何か他のものは、(たとえば、実際のパーサ、いくつかのサブストリングマジック、など)テキスト解析のより良い仕事をしている場合、それを使用しています。しかし、あなたは彼らと不快に感じるという理由だけで可能性を捨てていない - 他の人が少ないの問題は、それらに対処している可能性があり、すべての人が学ぶことができます。
。EDIT:mmyersによるコメントの後、正規表現を更新しました。
。ユーティリティクラスの場合は、チェックアウト java.util.Scannerする。あなたはあなたの問題を解決については行くかもしれない方法として、そこに多くのオプションがあります。私は、あなたの質問にいくつかのコメントがあります。
デバッガは、それらも(正規表現)を処理しません。
正規表現作品かどうかは、あなたのデータにいただきました!に依存します。あなたはのように、正規表現の構築を支援するために使用できるいくつかの素晴らしいプラグインがあります。 Eclipse用QuickREx には、デバッガは実際にあなたがあなたのデータのための右のパーサを書く際に役立つのでしょうか?
これらは、ソースコードを読んで誰かの流れを中断します。
私はそれはあなたがそれらをどのように快適に依存しますね。個人的に、私はむしろ、文字列解析コードの50行以上より合理的な正規表現を読んだが、多分それは個人的なことだ。
残業のが有機的に成長し、モンスターになっregexでます。
私は、彼らがかもしれないと思いますが、それはおそらく、彼らは非集束なりつつに住んでコードの問題です。ソースデータの複雑さが増加している場合は、おそらくあなたは(ANTLRのような多分パーサジェネレータ)
より表現ソリューションを必要とするかどうかに目を維持する必要があります彼らは深く非直感的です。
彼らは、パターンマッチング言語です。私は、彼らがそのコンテキストではかなり直感的だと言うでしょう。
私は上記のコードの可読性を向上させることができるか?
わからない、別に使用から正規表現ます。
これを行うには良い方法はありますか?エレガントにこの問題を解決Utilのクラスます。
上記、java.util.Scanner。
どこで正規表現を使用して、私は上に書いたものにsimpilar何かをコーディングの間に線を引くのですか?
個人的に私は合理的に簡単な何のために正規表現を使用します。
あなたが正規表現の可読性/保守性を向上させる方法を教えてください。
拡張する前に慎重に考えて、それはあなたがやっていることは明らかだように、詳細にコードや正規表現をコメントに余分な世話をします。
私はこの(警告、未テストコード)のようなものを使用します。私にとってこれは正規表現を避けるためにしようとするよりも多く読みやすいです。正規表現は、適切な場所で使用する最適なツールです。
も役立つ方法をコメントとコメント入力値と出力値の例を提供します。
List<String> digitsAsElements(String str){
Pattern p = Pattern.compile("(\\d+|\\w+)*");
Matcher m = p.matcher(str);
List<String> output = new ArrayList<String>();
for(int i = 1; i <= m.groupCount(); i++) {
output.add(m.group(i));
}
return output;
}
私は自分自身はRegexについて過度に狂っないんだけど、これは、彼らが実際に物事を簡単にする場合のように思えます。あなたは、工夫適切という名前を付け、その後、別の方法では、すべての制御コードを置くことができる最小の方法に入れて何をしたい場合がありますがされます。
あなたは「グラブブロック番号または文字の」方法をコード化された場合は、たとえば、呼び出し側はちょうど各呼び出しの結果を印刷する非常にシンプルな、ストレートフォワードループとなり、そしてあなたが呼んでいた方法がよくなります人々は時間をかけてそれをマックそうではありませんので、あなたは構文については何も知らなかった、と方法が制限されることになる場合でも、正規表現の意図は明らかであるように、定義されます。
これに伴う問題は、正規表現のツールは非常にシンプルで、それは、このためのメソッド呼び出しを正当化するのは難しいこの使用によく適応しているということです。
、私はそれに打撃を与えるでしょう。
まず非正規表現バージョン。私は、文字の種類は(数字または非桁)最後に見られた方蓄積するためのStringBuilderを使用することに注意してください。状態が変化した場合、私はリストにその内容をダンプし、新しいStringBuilderのを開始します。この方法は、連続する非数字が連続した数字であると同じようにグループ化されています。
static List<String> digitsAsElements(String str) {
StringBuilder collector = new StringBuilder();
List<String> output = new ArrayList<String>();
boolean lastWasDigit = false;
for (int i = 0; i < str.length(); i++) {
char cChar = str.charAt(i);
boolean isDigit = Character.isDigit(cChar);
if (isDigit != lastWasDigit) {
if (collector.length() > 0) {
output.add(collector.toString());
collector = new StringBuilder();
}
lastWasDigit = isDigit;
}
collector.append(cChar);
}
if (collector.length() > 0)
output.add(collector.toString());
return output;
}
これで正規表現バージョン。これは基本的にユハ・S.によって投稿されたものと同じコードですが、正規表現は、実際に動作ます。
private static final Pattern DIGIT_OR_NONDIGIT_STRING =
Pattern.compile("(\\d+|[^\\d]+)");
static List<String> digitsAsElementsR(String str) {
// Match a consecutive series of digits or non-digits
final Matcher matcher = DIGIT_OR_NONDIGIT_STRING.matcher(str);
final List<String> output = new ArrayList<String>();
while (matcher.find()) {
output.add(matcher.group());
}
return output;
}
私は読みやすい私の正規表現を維持しようとする一つの方法は、自分の名前です。私はDIGIT_OR_NONDIGIT_STRING
は、私(プログラマが)それがないと思うし、テストはそれは本当に行うことに意味何していることを確認する必要がありますどのようなかなりうまく伝えると考えています。
public static void main(String[] args) {
System.out.println(digitsAsElements( "34A312O5MNI444123A"));
System.out.println(digitsAsElementsR("34A312O5MNI444123A"));
}
プリントます:
[34, A, 312, O, 5, MNI, 444123, A] [34, A, 312, O, 5, MNI, 444123, A]
無し、誰かが勝った。と思い、正規表現版を読みやすい。また、異なっていることに注意出力との間に2つの実装でvsの予想出力...
出力:
digitsAsElements1("34A312O5MNI444123A") = [34, A, 312, O, 5, M, , N, , I, 444123, A]
digitsAsElements2("34A312O5MNI444123A") = [34, A, 312, O, 5, MNI, 444123, A]
Expected: [34, A, 312, O, 5, MN, 444123, A]
比較:
DigitsAsElements.java:
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class DigitsAsElements {
static List<String> digitsAsElements1(String str){
StringBuilder digitCollector = new StringBuilder();
List<String> output = new ArrayList<String>();
for (int i = 0; i < str.length(); i++){
char cChar = str.charAt(i);
if (Character.isDigit(cChar))
digitCollector.append(cChar);
else{
output.add(digitCollector.toString());
output.add(""+cChar);
digitCollector = new StringBuilder();
}
}
return output;
}
static List<String> digitsAsElements2(String str){
// Match a consecutive series of digits or non-digits
final Pattern pattern = Pattern.compile("(\\d+|\\D+)");
final Matcher matcher = pattern.matcher(str);
final List<String> output = new ArrayList<String>();
while (matcher.find()) {
output.add(matcher.group());
}
return output;
}
/**
* @param args
*/
public static void main(String[] args) {
System.out.println("digitsAsElements(\"34A312O5MNI444123A\") = " +
digitsAsElements1("34A312O5MNI444123A"));
System.out.println("digitsAsElements2(\"34A312O5MNI444123A\") = " +
digitsAsElements2("34A312O5MNI444123A"));
System.out.println("Expected: [" +
"34, A, 312, O, 5, MN, 444123, A"+"]");
}
}
あなたのループを簡単にするために、このクラスを使用することができます:
public class StringIterator implements Iterator<Character> {
private final char[] chars;
private int i;
private StringIterator(char[] chars) {
this.chars = chars;
}
public boolean hasNext() {
return i < chars.length;
}
public Character next() {
return chars[i++];
}
public void remove() {
throw new UnsupportedOperationException("Not supported.");
}
public static Iterable<Character> of(String string) {
final char[] chars = string.toCharArray();
return new Iterable<Character>() {
@Override
public Iterator<Character> iterator() {
return new StringIterator(chars);
}
};
}
}
今、あなたはこれを書き換えることができます:
for (int i = 0; i < str.length(); i++){
char cChar = str.charAt(i);
...
}
と
for (Character cChar : StringIterator.of(str)) {
...
}
私の2セント。
ところで、このクラスは、他の文脈で再利用可能である。