জাভা: কমা দ্বারা পৃথক স্ট্রিং বিভক্ত করা হচ্ছে কিন্তু উদ্ধৃতিগুলিতে কমাগুলি উপেক্ষা করা হচ্ছে


249

আমার কাছে এর মতো অস্পষ্ট স্ট্রিং রয়েছে:

foo,bar,c;qual="baz,blurb",d;junk="quux,syzygy"

যে আমি কমা দ্বারা বিভক্ত করতে চাই - কিন্তু আমার উদ্ধৃতিতে কমাগুলি উপেক্ষা করা দরকার। কিভাবে আমি এটি করতে পারব? একটি regexp পদ্ধতির ব্যর্থতা মত মনে হচ্ছে; আমি মনে করি আমি কোনও উদ্ধৃতি দেখলে আমি ম্যানুয়ালি স্ক্যান করতে এবং একটি অন্য মোডে প্রবেশ করতে পারি তবে প্রিক্সিটিং লাইব্রেরিগুলি ব্যবহার করা ভাল হবে। ( সম্পাদনা : আমি অনুমান করি যে আমার কাছে লাইব্রেরিগুলি ইতোমধ্যে জেডিকে বা ইতিমধ্যে অ্যাপাচি কমন্সের মতো সাধারণভাবে ব্যবহৃত লাইব্রেরির অংশ।

উপরের স্ট্রিংগুলিতে বিভক্ত হওয়া উচিত:

foo
bar
c;qual="baz,blurb"
d;junk="quux,syzygy"

দ্রষ্টব্য: এটি কোনও সিএসভি ফাইল নয়, এটি একটি বৃহত্তর সামগ্রিক কাঠামোযুক্ত কোনও ফাইলে থাকা একটি স্ট্রিং

উত্তর:


435

চেষ্টা করুন:

public class Main { 
    public static void main(String[] args) {
        String line = "foo,bar,c;qual=\"baz,blurb\",d;junk=\"quux,syzygy\"";
        String[] tokens = line.split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)", -1);
        for(String t : tokens) {
            System.out.println("> "+t);
        }
    }
}

আউটপুট:

> foo
> bar
> c;qual="baz,blurb"
> d;junk="quux,syzygy"

অন্য কথায়: কেবলমাত্র কমাতে বিভক্ত যদি সেই কমাতে শূন্য থাকে, বা এর আগে অনেকগুলি উদ্ধৃতি রয়েছে

বা, চোখের জন্য কিছুটা বন্ধুত্বপূর্ণ:

public class Main { 
    public static void main(String[] args) {
        String line = "foo,bar,c;qual=\"baz,blurb\",d;junk=\"quux,syzygy\"";

        String otherThanQuote = " [^\"] ";
        String quotedString = String.format(" \" %s* \" ", otherThanQuote);
        String regex = String.format("(?x) "+ // enable comments, ignore white spaces
                ",                         "+ // match a comma
                "(?=                       "+ // start positive look ahead
                "  (?:                     "+ //   start non-capturing group 1
                "    %s*                   "+ //     match 'otherThanQuote' zero or more times
                "    %s                    "+ //     match 'quotedString'
                "  )*                      "+ //   end group 1 and repeat it zero or more times
                "  %s*                     "+ //   match 'otherThanQuote'
                "  $                       "+ // match the end of the string
                ")                         ", // stop positive look ahead
                otherThanQuote, quotedString, otherThanQuote);

        String[] tokens = line.split(regex, -1);
        for(String t : tokens) {
            System.out.println("> "+t);
        }
    }
}

যা প্রথম উদাহরণ হিসাবে একই উত্পাদন করে।

সম্পাদনা

মন্তব্যগুলিতে @ মাইকএফএই দ্বারা উল্লিখিত:

আমি পেয়ারা স্প্লিটটার ব্যবহার পছন্দ করি , কারণ এতে ডিফল্ট রয়েছে (খালি ম্যাচগুলি ছাঁটাই করা সম্পর্কে উপরে আলোচনা দেখুন String#split(), তাই আমি করেছি:

Splitter.on(Pattern.compile(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)"))

আরএফসি 4180: সেক 2.6 অনুসারে: "লাইন ব্রেক (সিআরএলএফ), ডাবল কোট এবং কমাগুলি সম্বলিত ক্ষেত্রগুলি ডাবল-কোটে আবদ্ধ থাকা উচিত।" সেক 2.7: "যদি ক্ষেত্রগুলি আবদ্ধ করতে ডাবল-কোট ব্যবহার করা হয়, তবে ক্ষেত্রের ভিতরে উপস্থিত একটি ডাবল উদ্ধৃতিটি এর আগে অন্য একটি ডাবল উদ্ধৃতি দিয়ে এড়াতে হবে" সুতরাং, String line = "equals: =,\"quote: \"\"\",\"comma: ,\""আপনাকে যা করতে হবে তা হ'ল বহিরাগত ডাবল উদ্ধৃতিটি বন্ধ করে দেওয়া উচিত চরিত্র.
পল হ্যানবুরি

@ বার্ট: আমার বক্তব্যটি হ'ল আপনার সমাধানটি এখনও এম্বেডড কোট সহ কার্যকরভাবে কাজ করে
পল হ্যানবারি

6
@Alex হাঁ, কমা হয় মিলেছে, কিন্তু খালি ম্যাচ ফলাফলে নয়। যোগ -1বিভক্ত পদ্ধতি PARAM করুন: line.split(regex, -1)। দেখুন: docs.oracle.com/javase/6/docs/api/java/lang/…
বার্ট

2
দুর্দান্ত কাজ! আমি পেয়ারা স্প্লিটার ব্যবহার পছন্দ করি, কারণ এতে স্যানার ডিফল্ট রয়েছে (স্ট্রিং # স্প্লিট দ্বারা খালি ম্যাচগুলি ছাঁটাই করা সম্পর্কে উপরে আলোচনা দেখুন), তাই আমিও করেছি Splitter.on(Pattern.compile(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)"))
মাইকএফহে

2
সতর্কতামূলক !!!! এই regexp ধীর !!! এর ও (এন ^ 2) আচরণ রয়েছে যাতে প্রতিটি কমাতে লুক্কায়িত স্ট্রিংয়ের শেষ পর্যন্ত সমস্ত দিক দেখায়। এই regexp ব্যবহারের ফলে বড় স্পার্ক জবগুলিতে 4x ধীরগতির সৃষ্টি হয় (উদাহরণস্বরূপ 45 মিনিট -> 3 ঘন্টা)। দ্রুত বিকল্প হ'ল findAllIn("(?s)(?:\".*?\"|[^\",]*)*")প্রতিটি শূন্য ক্ষেত্র অনুসরণ করে প্রথম (সর্বদা শূন্য) ক্ষেত্রটি এড়াতে পোস্টপ্রোসেসিং পদক্ষেপের সাথে মিশ্রণের মতো কিছু something
নগর ওয়াগাবন্ড

46

যদিও আমি সাধারণভাবে নিয়মিত মত প্রকাশের মতোই করি, এই ধরণের রাষ্ট্র-নির্ভর টোকেনাইজেশনের জন্য আমি বিশ্বাস করি একটি সাধারণ পার্সার (যা এই শব্দটির দ্বারা এটি সহজ শব্দটির চেয়ে অনেক সহজ) সম্ভবত এটি একটি পরিষ্কার সমাধান, বিশেষত রক্ষণাবেক্ষণের ক্ষেত্রে যেমন:

String input = "foo,bar,c;qual=\"baz,blurb\",d;junk=\"quux,syzygy\"";
List<String> result = new ArrayList<String>();
int start = 0;
boolean inQuotes = false;
for (int current = 0; current < input.length(); current++) {
    if (input.charAt(current) == '\"') inQuotes = !inQuotes; // toggle state
    boolean atLastChar = (current == input.length() - 1);
    if(atLastChar) result.add(input.substring(start));
    else if (input.charAt(current) == ',' && !inQuotes) {
        result.add(input.substring(start, current));
        start = current + 1;
    }
}

যদি আপনি কোটের ভিতরে কমা সংরক্ষণের বিষয়ে চিন্তা না করেন তবে আপনি এই পদ্ধতিকে সহজ করতে পারবেন (সূচনা সূচকের কোনও পরিচালনা নয়, কোনও শেষ চরিত্রের বিশেষ ক্ষেত্রে নেই) আপনার কমাগুলিকে অন্য কোনও কোট দ্বারা প্রতিস্থাপন করে এবং তারপর কমাতে বিভক্ত করুন:

String input = "foo,bar,c;qual=\"baz,blurb\",d;junk=\"quux,syzygy\"";
StringBuilder builder = new StringBuilder(input);
boolean inQuotes = false;
for (int currentIndex = 0; currentIndex < builder.length(); currentIndex++) {
    char currentChar = builder.charAt(currentIndex);
    if (currentChar == '\"') inQuotes = !inQuotes; // toggle state
    if (currentChar == ',' && inQuotes) {
        builder.setCharAt(currentIndex, ';'); // or '♡', and replace later
    }
}
List<String> result = Arrays.asList(builder.toString().split(","));

স্ট্রিংকে বিশ্লেষণ করার পরে বিশ্লেষণকারী টোকেনগুলি থেকে উদ্ধৃতিগুলি সরানো উচিত।
সুধীর এন

গুগলের মাধ্যমে পাওয়া গেছে, দুর্দান্ত অ্যালগরিদম ব্রো, সহজ এবং মানিয়ে নেওয়া সহজ, সম্মত। রাষ্ট্রীয় স্টাফগুলি পার্সারের মাধ্যমে করা উচিত, রেজেক্স একটি জগাখিচুড়ি।
রুডলফ শ্মিড্ট

2
মনে রাখবেন যে কমা যদি সর্বশেষ চরিত্র হয় তবে এটি সর্বশেষ আইটেমের স্ট্রিং মানে থাকবে।
গ্যাব্রিয়েল গেটস

21

http://sourceforge.net/projects/javacsv/

https://github.com/pupi1985/ জাভা সিএসভি- পুনরায় লোড করা (পূর্ববর্তী লাইব্রেরির কাঁটাচামচ \r\nযা উইন্ডোজ চালু না করার সময় উত্পন্ন আউটপুটটিতে উইন্ডোজ লাইন টার্মিনেটর রাখতে দেয় )

http://opencsv.sourceforge.net/

জাভার জন্য সিএসভি এপিআই

আপনি কি সিএসভি ফাইলগুলি পড়ার (এবং সম্ভবত লেখার জন্য) জাভা গ্রন্থাগারের সুপারিশ করতে পারেন?

সিএসভি কে এক্সএমএল রূপান্তর করতে জাভা লিবিব বা অ্যাপ্লিকেশন?


3
ওপ একটি সিএসভি ফাইল পার্স করে যাচ্ছিল তা বুঝতে পেরে ভাল কল। একটি বাহ্যিক গ্রন্থাগার এই কাজের জন্য অত্যন্ত উপযুক্ত।
স্টেফান কেন্ডাল

1
তবে স্ট্রিংটি একটি সিএসভি স্ট্রিং; আপনার সরাসরি স্ট্রিংয়ে কোনও সিএসভি এপিআই ব্যবহার করতে সক্ষম হওয়া উচিত।
মাইকেল ব্রুয়ার-ডেভিস

হ্যাঁ, তবে এই কাজটি যথেষ্ট সহজ এবং বৃহত্তর অ্যাপ্লিকেশনটির অনেক ছোট অংশ, যা আমি অন্য বাহ্যিক লাইব্রেরিতে টানতে পছন্দ করি না।
জেসন এস

7
অগত্যা নয় ... আমার দক্ষতা প্রায়শই পর্যাপ্ত, তবে তারা সম্মানিত হওয়া থেকে উপকৃত হয়।
জেসন এস

9

আমি বার্টের কাছ থেকে একটি রেইজেক্স উত্তরের পরামর্শ দেব না, আমি এই বিশেষ ক্ষেত্রে পার্সিং সমাধানটি আরও ভাল পাই (যেমন ফ্যাবিয়ান প্রস্তাবিত)। আমি রেজেক্স সলিউশন এবং নিজস্ব পার্সিং বাস্তবায়ন চেষ্টা করেছি আমি এটি পেয়েছি:

  1. ব্যাকরিফারেন্স সহ রেগেক্সের সাথে বিভক্ত হওয়ার চেয়ে পার্সিং অনেক দ্রুত - সংক্ষিপ্ত স্ট্রিংয়ের জন্য times 20 গুণ দ্রুত, দীর্ঘ স্ট্রিংয়ের জন্য ~ 40 গুণ দ্রুত।
  2. রেজিেক্স শেষ কমা পরে খালি স্ট্রিং খুঁজে পেতে ব্যর্থ। যদিও এটি মূল প্রশ্নে ছিল না, এটি আমার প্রয়োজন।

আমার সমাধান এবং নীচে পরীক্ষা।

String tested = "foo,bar,c;qual=\"baz,blurb\",d;junk=\"quux,syzygy\",";
long start = System.nanoTime();
String[] tokens = tested.split(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)");
long timeWithSplitting = System.nanoTime() - start;

start = System.nanoTime(); 
List<String> tokensList = new ArrayList<String>();
boolean inQuotes = false;
StringBuilder b = new StringBuilder();
for (char c : tested.toCharArray()) {
    switch (c) {
    case ',':
        if (inQuotes) {
            b.append(c);
        } else {
            tokensList.add(b.toString());
            b = new StringBuilder();
        }
        break;
    case '\"':
        inQuotes = !inQuotes;
    default:
        b.append(c);
    break;
    }
}
tokensList.add(b.toString());
long timeWithParsing = System.nanoTime() - start;

System.out.println(Arrays.toString(tokens));
System.out.println(tokensList.toString());
System.out.printf("Time with splitting:\t%10d\n",timeWithSplitting);
System.out.printf("Time with parsing:\t%10d\n",timeWithParsing);

অবশ্যই আপনি যদি এই কৃপণতা নিয়ে অস্বস্তি বোধ করেন তবে এই স্নিপেটে অন্য-আইফোগুলিতে স্যুইচ পরিবর্তন করতে পারেন। নোট তারপর বিভাজকের সাথে স্যুইচ পরে বিরতি অভাব। স্ট্রিংবিল্ডার গতি বাড়াতে ডিজাইন করে স্ট্রিংবাফারের পরিবর্তে বেছে নেওয়া হয়েছিল, যেখানে থ্রেড সুরক্ষা অপ্রাসঙ্গিক।


2
সময় বিভাজন বনাম পার্সিং সম্পর্কিত আকর্ষণীয় পয়েন্ট। তবে বিবৃতি # 2 ভুল। যদি আপনি -1বার্টের উত্তরে বিভাজন পদ্ধতিতে যুক্ত হন, আপনি খালি স্ট্রিংগুলি (শেষ কমাতে খালি স্ট্রিং সহ) পাবেন:line.split(regex, -1)
পিটার

+1 কারণ এটি যে সমস্যার জন্য আমি সমাধানটি সন্ধান করছিলাম এটির জন্য এটি আরও ভাল সমাধান: একটি জটিল এইচটিটিপি পোস্টের বডি প্যারামিটার স্ট্রিংকে পার্সিং করা
ভেরন্ট্রন

2

মত দেখার চেষ্টা করুন (?!\"),(?!\")। এটি ,ঘিরে নেই এমনটি মিলবে "


খুব নিশ্চিত যে এটি একটি তালিকার মতো ভেঙে যাবে: "ফু", বার, "বাজ"
অ্যাঞ্জেলো জেনোভেস

1
আমি মনে করি আপনি বোঝাতে চেয়েছিলেন (?<!"),(?!")তবে এটি এখনও কার্যকর হবে না। স্ট্রিংটি দেওয়া হয়েছে one,two,"three,four", এটি কমাতে সঠিকভাবে মেলে one,twoতবে এটি কমাতে "three,four"মেলে এবং একটিতে মেলে না two,"three
অ্যালান মুর

এটি আমার জন্য নিখুঁতভাবে কাজ করার seams, IMHO আমি মনে করি এটির সংক্ষিপ্ত এবং আরও সহজে বোধগম্য হওয়ার কারণে এটি আরও ভাল উত্তর
অর্ডিয়েল

2

আপনি সেই বিরক্তিকর সীমানা অঞ্চলে রয়েছেন যেখানে রিজেক্সপস প্রায় না করে (বার্ট দ্বারা চিহ্নিত করা হয়েছে, উদ্ধৃতিগুলি থেকে বেরিয়ে আসা জীবনকে আরও কঠিন করে তুলবে), এবং তবুও একটি পূর্ণ-বর্ধমান পার্সার ওভারকিলের মতো মনে হয়।

আপনার যদি কোনও সময় আরও জটিলতার প্রয়োজন হয় তবে খুব শীঘ্রই আমি একটি পার্সার লাইব্রেরি সন্ধান করতে যাব। উদাহরণস্বরূপ এই এক


2

আমি অধৈর্য হয়েছি এবং উত্তরের জন্য অপেক্ষা না করা বেছে নিয়েছিলাম ... রেফারেন্সের জন্য এমন কিছু করা খুব কঠিন মনে হচ্ছে না (যা আমার অ্যাপ্লিকেশনটির জন্য কাজ করে, আমার উদ্ধৃতি উদ্ধৃতি সম্পর্কে চিন্তা করার দরকার নেই, যেমন উদ্ধৃতিতে থাকা স্টাফ কয়েকটি সীমাবদ্ধ ফর্মের মধ্যে সীমাবদ্ধ):

final static private Pattern splitSearchPattern = Pattern.compile("[\",]"); 
private List<String> splitByCommasNotInQuotes(String s) {
    if (s == null)
        return Collections.emptyList();

    List<String> list = new ArrayList<String>();
    Matcher m = splitSearchPattern.matcher(s);
    int pos = 0;
    boolean quoteMode = false;
    while (m.find())
    {
        String sep = m.group();
        if ("\"".equals(sep))
        {
            quoteMode = !quoteMode;
        }
        else if (!quoteMode && ",".equals(sep))
        {
            int toPos = m.start(); 
            list.add(s.substring(pos, toPos));
            pos = m.end();
        }
    }
    if (pos < s.length())
        list.add(s.substring(pos));
    return list;
}

(পাঠকের জন্য অনুশীলন: ব্যাকস্ল্যাশগুলিও সন্ধান করে পালানো উক্তিগুলি পরিচালনা করতে প্রসারিত করুন))


1

সহজ পদ্ধিতি হ'ল ডিলিমিটারদের সাথে মেলে না, অর্থাত্ কমাগুলি, কী কী প্রকৃত উদ্দেশ্য (যা উপাত্তগুলি উদ্ধৃত করা যেতে পারে স্ট্রিং করা যেতে পারে) এর সাথে মিলানোর জন্য একটি জটিল অতিরিক্ত যুক্তি সহ, কেবলমাত্র মিথ্যা সীমানা বাদকারীকে বাদ দিতে নয়, বরং প্রথম স্থানে চিহ্নিত ডেটাটি মেলানো।

প্যাটার্নটিতে দুটি বিকল্প রয়েছে, একটি উদ্ধৃত স্ট্রিং ( "[^"]*"বা ".*?") বা পরবর্তী কমা পর্যন্ত সমস্ত কিছু ( [^,]+)। খালি ঘরগুলি সমর্থন করার জন্য, আমাদের অব্যক্ত আইটেমটি খালি থাকতে দেওয়া উচিত এবং পরবর্তী কমাটি, যদি কোনও হয় তবে সেবন করতে হবে এবং \\Gঅ্যাঙ্করটি ব্যবহার করতে হবে :

Pattern p = Pattern.compile("\\G\"(.*?)\",?|([^,]*),?");

প্যাটার্নটিতে দুটি পেতে ক্যাপচারিং গ্রুপও রয়েছে, উদ্ধৃত স্ট্রিংয়ের সামগ্রী বা সরল সামগ্রী।

তারপরে, জাভা 9 এর সাহায্যে আমরা একটি অ্যারে পেতে পারি

String[] a = p.matcher(input).results()
    .map(m -> m.group(m.start(1)<0? 2: 1))
    .toArray(String[]::new);

যদিও পুরানো জাভা সংস্করণগুলির মতো লুপের প্রয়োজন

for(Matcher m = p.matcher(input); m.find(); ) {
    String token = m.group(m.start(1)<0? 2: 1);
    System.out.println("found: "+token);
}

একটি Listবা একটি অ্যারেতে আইটেমগুলি যুক্ত করা পাঠকের কাছে আবগারি হিসাবে ছেড়ে যায়।

জাভা 8 এর জন্য, আপনি জাভা 9 সমাধানের মতো এটি করতে এই উত্তরটিরresults() বাস্তবায়নটি ব্যবহার করতে পারেন ।

এম্বেড থাকা স্ট্রিং সহ মিশ্র সামগ্রীর জন্য, যেমন প্রশ্নের মধ্যে, আপনি কেবল ব্যবহার করতে পারেন

Pattern p = Pattern.compile("\\G((\"(.*?)\"|[^,])*),?");

তবে তারপরে, স্ট্রিংগুলি তাদের উদ্ধৃত আকারে রাখা হয়।


0

লুকহ্যাড এবং অন্যান্য ক্রেজিট রেগেক্স ব্যবহার না করে প্রথমে উদ্ধৃতিগুলি টানুন। এটি হ'ল প্রতিটি উদ্ধৃতি গোষ্ঠীকরণের জন্য, সেই গোষ্ঠীকরণটি __IDENTIFIER_1বা অন্য কোনও সূচকের সাথে প্রতিস্থাপন করুন এবং সেই গোষ্ঠীটিকে স্ট্রিং, স্ট্রিংয়ের মানচিত্রে মানচিত্র করুন।

আপনি কমাতে বিভক্ত হওয়ার পরে, সমস্ত ম্যাপযুক্ত শনাক্তকারীকে মূল স্ট্রিং মানগুলির সাথে প্রতিস্থাপন করুন।


এবং কীভাবে ক্রেজিট রেগেক্সস ছাড়াই উদ্ধৃতি গোষ্ঠীগুলি সন্ধান করবেন?
কাই হাপম্যান

প্রতিটি চরিত্রের জন্য, যদি চরিত্রটি উদ্ধৃতি হয়, পরবর্তী উদ্ধৃতিটি সন্ধান করুন এবং গ্রুপিংয়ের সাথে প্রতিস্থাপন করুন। যদি পরবর্তী কোন উদ্ধৃতি না হয়।
স্টিফান কেন্ডাল

0

স্ট্রিং.স্প্লিট () ব্যবহার করে ওয়ান-লাইনার সম্পর্কে কী?

String s = "foo,bar,c;qual=\"baz,blurb\",d;junk=\"quux,syzygy\"";
String[] split = s.split( "(?<!\".{0,255}[^\"]),|,(?![^\"].*\")" );

-1

আমি এরকম কিছু করব:

boolean foundQuote = false;

if(charAtIndex(currentStringIndex) == '"')
{
   foundQuote = true;
}

if(foundQuote == true)
{
   //do nothing
}

else 

{
  string[] split = currentString.split(',');  
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.