سؤال

لدي سلسلة التي تحتوي على الأرقام و الحروف.وأود أن تقسيم السلسلة إلى قطع متجاورة من أرقام متجاورة قطع من الحروف.

النظر في سلسلة "34A312O5M444123A".

أود أن الإخراج:["34" ، "" ، "312", "س", "5", "م", "444123"،""]

لدي الكود الذي يعمل ويبدو مثل:

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 نمط أجد أنماط regex يكون عقبة رئيسية القراءة.

  • مصححات لا التعامل معها بشكل جيد.
  • أنها يقطع تدفق شخص يقرأ التعليمات البرمجية المصدر.
  • العمل الإضافي regex تنمو عضويا و تصبح الوحوش.
  • هم بعمق غير بديهية.

أسئلتي هي:

  • كيف يمكنني تحسين القراءة من فوق الرمز ؟
  • هل هناك طريقة أفضل للقيام بذلك ؟ أ Util الدرجة أن يحل هذه المشكلة بشكل أنيق.
  • أين نرسم الخط الفاصل بين باستخدام التعابير المنطقية و الترميز شيء simpilar إلى ما كتبته أعلاه ؟
  • كيف يمكنك زيادة القراءة/الصيانة من regExes?
هل كانت مفيدة؟

المحلول

هل تكون على استعداد لاستخدام Regexes إذا كان ذلك يعني حل المشكلة في سطر واحد من التعليمات البرمجية؟

// 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)");

مع التعليق لشرح Regex، أعتقد أن هذا أكثر قابلية للقراءة من أي حلول غير Regex (أو أي من حلول Regex الأخرى، لهذه المسألة).

نصائح أخرى

بالنسبة لهذه المهمة الخاصة، كنت أستخدم دائما Regex بدلا من كتابة شيء مشابه. الرمز الذي قدمته أعلاه هو، على الأقل بالنسبة لي، أقل قابلية للقراءة من التعبير المنتظم البسيط (الذي سيكون (\d+|[^\d]+) في هذه الحالة، بقدر ما أستطيع أن أرى).

قد ترغب في تجنب كتابة التعبيرات العادية التي تتجاوز بعض الأسطر. تلك يمكن أن تكون وعادة ما تكون غير مقننة ويصعب فهمها، ولكن كذلك هو الرمز الذي يمكن استبداله به! لا تكون المحللون أبدا أبدا جميلة وأنت عادة ما تكون أفضل حالا في قراءة القواعد الأصلية من محاولة فهم المحلل المولد (أو بخط اليد). ينطبق الشيء نفسه (IMHO) عن Regexes التي هي مجرد وصف موجز للقواعد الناقصة.

لذلك، بشكل عام أقول حظر Regyxes لصالح الكود كما لو كنت قد قدمت في سؤالك يبدو وكأنه فكرة غبية بشكل رهيب. وتعبيرات منتظمة هي مجرد أداة، لا شيء أقل، لا شيء أكثر من ذلك. إذا قام شيء آخر بعمل أفضل في تحليل النص (قل، محلل حقيقي، بعض السحر الفرعي، وما إلى ذلك) ثم استخدمه. ولكن لا تتخلص من الاحتمالات فقط لأنك تشعر بعدم الارتياح معهم - قد يكون لدى الآخرين مشاكل أقل تعامل معهم وجميع الناس قادرون على التعلم.

تحرير: تحديث Regex بعد التعليق بواسطة mmyers.

عن فئة فائدة, تحقق java.util.الماسح الضوئي.هناك عدد من الخيارات في كيفية كنت قد تذهب نحو حل المشكلة الخاصة بك.لدي بعض التعليقات على الأسئلة الخاصة بك.

مصححات لا التعامل معها (التعبيرات العادية) حسنا

سواء regex يعمل أو لا يعتمد على ما في البيانات الخاصة بك.هناك بعض الإضافات التي يمكنك استخدامها لمساعدتك في بناء التعابير المنطقية ، مثل QuickREx عن الكسوف لا مصحح تساعد في الواقع كنت أكتب حق محلل على البيانات الخاصة بك ؟

أنها يقطع تدفق شخص يقرأ التعليمات البرمجية المصدر.

أعتقد أن ذلك يعتمد على كيف مريحة أنت معهم.انا شخصيا افضل قراءة معقول regex من 50 أكثر من الخطوط من سلسلة تحليل التعليمات البرمجية ، ولكن ربما هذا شيء شخصي.

العمل الإضافي regex تنمو عضويا و تصبح الوحوش.

أعتقد أنهم ربما, ولكن هذا ربما مشكلة مع رمز يعيشون في أن تصبح unfocussed.إذا تعقيد البيانات المصدر زيادة, ربما كنت بحاجة إلى إبقاء العين على ما إذا كنت بحاجة إلى أكثر تعبيرا الحل (ربما محلل مولد مثل ANTLR)

هم بعمق غير بديهية.

إنهم مطابقة نمط اللغة.أود أن أقول أنهم بديهية جدا في هذا السياق.

كيف يمكنني تحسين القراءة من فوق الرمز ؟

غير متأكد ، وبصرف النظر عن استخدام التعابير المنطقية.

هل هناك طريقة أفضل للقيام بذلك ؟ أ Util الدرجة أن يحل هذه المشكلة بشكل أنيق.

المذكورة أعلاه, java.util.الماسح الضوئي.

أين نرسم الخط الفاصل بين باستخدام التعابير المنطقية و الترميز شيء simpilar إلى ما كتبته أعلاه ؟

أنا شخصيا استخدام التعابير المنطقية لأي شيء معقول بسيطة.

كيف يمكنك زيادة القراءة/الصيانة من regExes?

التفكير بعناية قبل تقديم ، اتخاذ المزيد من الحيطة التعليق فوق رمز و regex في التفاصيل بحيث انها واضحة ما تفعله.

أود استخدام شيء مثل هذا (تحذير، رمز غير مجفف). بالنسبة لي هذا هو أكثر قابلية للقراءة بكثير من محاولة تجنب Regexps. Regexps هي أداة رائعة عند استخدامها في المكان المناسب.

تساعد الطرق التعليق وتوفير أمثلة من قيم المدخلات والمخرجات في التعليقات أيضا.

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 نفسي، لكن هذا يبدو وكأنه حالة ستبسط حقا الأشياء. ما قد ترغب في القيام به هو وضعها في أصغر طريقة يمكنك وضعها واسمها بشكل مناسب، ثم ضع كل رمز التحكم بطريقة أخرى.

على سبيل المثال، إذا قمت بترميز "كتلة الاستيلاء على الأرقام أو الحروف"، فإن المتصل سيكون حلقة بسيطة للغاية إلى الأمام فقط طباعة نتائج كل مكالمة، والطريقة التي كنت تتصل بها ستكون محددة جيدا لذلك ستكون نية Regex واضحة حتى لو كنت لا تعرف أي شيء عن بناء الجملة، وستحد الطريقة حتى لا يحتمل أن يتحول الناس بمرور الوقت.

المشكلة في هذه هي أن أدوات Regex هي بسيطة للغاية ومتكيفة جيدا لهذا الاستخدام، من الصعب تبرير طريقة استدعاء طريقة لهذا الغرض.

نظرا لأنه لا يبدو أنه قد نشر التعليمات البرمجية الصحيحة حتى الآن، سأعطيه طلقة.

أولا النسخة غير 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;
}

الآن نسخة Regex. هذا هو في الأساس نفس الكود الذي تم نشره بواسطة Juha S.، ولكن Regex يعمل بالفعل.

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;
}

إحدى طرق أحاول الحفاظ على مريحي Regexes مقروءة هي أسمائها. أظن 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

Awww، شخص فازني على الكود. أعتقد أن إصدار Regex أسهل للقراءة / صيانة. أيضا، لاحظ الفرق في الإخراج بين التطبيقين 2 مقابل الإخراج المتوقع ...

انتاج:

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]

قارن:

Digitsasaselements.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 سنتا

راجع للشغل هذه الفئة قابلة لإعادة الاستخدام في السياق الآخر.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top