كيفية تقسيم سلسلة مفصولة فاصلة مع تجاهل الفواصل النار؟

StackOverflow https://stackoverflow.com/questions/820172

  •  03-07-2019
  •  | 
  •  

سؤال

أحتاج إلى كتابة نسخة ممتدة من وظيفة StringUtils.CommAdelimitedListTtringArray التي تحصل على معلمة إضافية: Escape Char.

لذا استدعاء بلدي:

commaDelimitedListToStringArray("test,test\\,test\\,test,test", "\\")

يجب أن تعود:

["test", "test,test,test", "test"]



محاولتي الحالية هي استخدام string.split () لتقسيم السلسلة باستخدام التعبيرات العادية:

String[] array = str.split("[^\\\\],");

لكن الصفيف الذي تم إرجاعه هو:

["tes", "test\,test\,tes", "test"]

أيه أفكار؟

هل كانت مفيدة؟

المحلول

التعبير العادي

[^\\],

يعني "تطابق شخصية ليست عكسية تليها فاصلة" - لهذا السبب مثل أنماط مثل t, مطابقة لأن t هي شخصية ليست عكسية.

أعتقد أنك بحاجة إلى استخدام نوع من نظرة سلبية, ، لالتقاط أ , الذي لا يسبقه أ \ دون التقاط الشخصية السابقة ، شيء مثل

(?<!\\),

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

نصائح أخرى

محاولة:

String array[] = str.split("(?<!\\\\),");

في الأساس ، هذا يقول انقسام على فاصلة ، باستثناء المكان الذي يسبقه الفاصلة من قبل اثنين من الذرات الخلفية. وهذا ما يسمى أ نظرة سلبية على التأكيد على عرض الصفر.

للرجوع إليها في المستقبل ، إليك الطريقة الكاملة التي انتهى بها الأمر إلى:

public static String[] commaDelimitedListToStringArray(String str, String escapeChar) {
    // these characters need to be escaped in a regular expression
    String regularExpressionSpecialChars = "/.*+?|()[]{}\\";

    String escapedEscapeChar = escapeChar;

    // if the escape char for our comma separated list needs to be escaped 
    // for the regular expression, escape it using the \ char
    if(regularExpressionSpecialChars.indexOf(escapeChar) != -1) 
        escapedEscapeChar = "\\" + escapeChar;

    // see http://stackoverflow.com/questions/820172/how-to-split-a-comma-separated-string-while-ignoring-escaped-commas
    String[] temp = str.split("(?<!" + escapedEscapeChar + "),", -1);

    // remove the escapeChar for the end result
    String[] result = new String[temp.length];
    for(int i=0; i<temp.length; i++) {
        result[i] = temp[i].replaceAll(escapedEscapeChar + ",", ",");
    }

    return result;
}

كما قال مات ب ، [^\\], سوف يفسر الشخصية التي سبقت فاصلة كجزء من المحدد.

"test\\\\\\,test\\\\,test\\,test,test"
  -(split)->
["test\\\\\\,test\\\\,test\\,tes" , "test"]

كما قال drvdijk ، (?<!\\), سوف يسيء تفسير الهروب من الخلف.

"test\\\\\\,test\\\\,test\\,test,test"
  -(split)->
["test\\\\\\,test\\\\,test\\,test" , "test"]
  -(unescape commas)->
["test\\\\,test\\,test,test" , "test"]

أتوقع أن أتمكن من الهروب من الخلفاء أيضًا ...

"test\\\\\\,test\\\\,test\\,test,test"
  -(split)->
["test\\\\\\,test\\\\" , "test\\,test" , "test"]
  -(unescape commas and backslashes)->
["test\\,test\\" , "test,test" , "test"]

اقترح drvdijk (?<=(?<!\\\\)(\\\\\\\\){0,100}), الذي يعمل بشكل جيد للقوائم مع العناصر التي تنتهي بما يصل إلى 100 خطوط خلفية. هذا بعيد بما فيه الكفاية ... ولكن لماذا الحد؟ هل هناك طريقة أكثر كفاءة (لا تبدو الجشع)؟ ماذا عن الأوتار غير الصالحة؟

لقد بحثت لفترة من الوقت عن حل عام ، ثم كتبت الشيء بنفسي ... الفكرة هي الانقسام بعد نمط يطابق عناصر القائمة (بدلاً من مطابقة المحدد).

إجابتي لا تأخذ شخصية الهروب كمعلمة.

public static List<String> commaDelimitedListStringToStringList(String list) {
    // Check the validity of the list
    // ex: "te\\st" is not valid, backslash should be escaped
    if (!list.matches("^(([^\\\\,]|\\\\,|\\\\\\\\)*(,|$))+")) {
        // Could also raise an exception
        return null;
    }
    // Matcher for the list elements
    Matcher matcher = Pattern
            .compile("(?<=(^|,))([^\\\\,]|\\\\,|\\\\\\\\)*(?=(,|$))")
            .matcher(list);
    ArrayList<String> result = new ArrayList<String>();
    while (matcher.find()) {
        // Unescape the list element
        result.add(matcher.group().replaceAll("\\\\([\\\\,])", "$1"));
    }
    return result;
}

وصف للنمط (غير معروف):

(?<=(^|,)) الأمام هو بداية السلسلة أو أ ,

([^\\,]|\\,|\\\\)* العنصر المكون من \,, \\ أو الشخصيات ليست كذلك \ ولا ,

(?=(,|$)) خلف نهاية السلسلة أو أ ,

قد يتم تبسيط النمط.

حتى مع 3 أخصائيات (matches + find + replaceAll) ، تبدو هذه الطريقة أسرع من الطريقة التي اقترحتها DRVDIJK. لا يزال من الممكن تحسينه عن طريق كتابة محلل محدد معين.

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

public static List<String> commaDelimitedListStringToStringList2(String list) {
    if (!list.matches("^(([^,]|,,)*(,|$))+")) {
        return null;
    }
    Matcher matcher = Pattern.compile("(?<=(^|,))([^,]|,,)*(?=(,|$))")
                    .matcher(list);
    ArrayList<String> result = new ArrayList<String>();
    while (matcher.find()) {
        result.add(matcher.group().replaceAll(",,", ","));
    }
    return result;
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top