টিএসভি পাঠ্য পার্স করার জন্য আমি কীভাবে একটি রাকু ব্যাকরণকে সংজ্ঞায়িত করতে পারি?


13

আমার কাছে কিছু টিএসভি ডেটা রয়েছে

ID     Name    Email
   1   test    test@email.com
 321   stan    stan@nowhere.net

আমি এটি হ্যাশগুলির একটি তালিকায় পার্স করতে চাই

@entities[0]<Name> eq "test";
@entities[1]<Email> eq "stan@nowhere.net";

মান সারি থেকে শিরোনাম সীমাটি সীমিত করতে নিউলাইন মেটাচার্যাক্টর ব্যবহার করে আমার সমস্যা হচ্ছে। আমার ব্যাকরণ সংজ্ঞা:

use v6;

grammar Parser {
    token TOP       { <headerRow><valueRow>+ }
    token headerRow { [\s*<header>]+\n }
    token header    { \S+ }
    token valueRow  { [\s*<value>]+\n? }
    token value     { \S+ }
}

my $dat = q:to/EOF/;
ID     Name    Email
   1   test    test@email.com
 321   stan    stan@nowhere.net
EOF
say Parser.parse($dat);

কিন্তু এই ফিরে আসছে Nil। আমি মনে করি আমি রাকুতে রেগেক্সগুলি সম্পর্কে মৌলিক কিছু ভুল বুঝছি।


1
Nil। প্রতিক্রিয়া হিসাবে এটি বেশ বন্ধ্যা হয়, তাই না? ডিবাগ, ডাউনলোডের জন্য commaide যদি আপনি ইতিমধ্যেই না করে, এবং / অথবা দেখতে ব্যাকরণ প্রতিবেদন উন্নত করা ত্রুটি পারেন কিভাবে? । আপনি Nilআপনার প্যাটার্নটি ব্যাকট্র্যাকিং শব্দার্থবিজ্ঞান ধরে নিয়েছেন। সে সম্পর্কে আমার উত্তর দেখুন। আমি আপনাকে ব্যাকট্র্যাকিং বন্ধ করার পরামর্শ দিচ্ছি। সে সম্পর্কে @ user0721090601 এর উত্তর দেখুন। নিখুঁত ব্যবহারিকতা এবং গতির জন্য জেজে উত্তর দেখুন see এছাড়াও, "আমি রাকুর সাথে এক্স পার্স করতে চাই। এর জন্য কেউ পরিচিত হতে পারে?"
রায়ফ

ব্যাকরণ ব্যবহার করুন :: ট্রেসার; আমার জন্য # ওয়ার্কস
পি

উত্তর:


12

সম্ভবত এটি মুছে ফেলার মূল বিষয়টি হ'ল \sঅনুভূমিক এবং উল্লম্ব স্থানের সাথে মেলে । মাত্র অনুভূমিক স্থান মেলে, ব্যবহার করুন \h, এবং ঠিক উল্লম্ব স্থান মেলে \v,।

আমার করা একটি ছোট প্রস্তাবনাটি হ'ল টোকেনের নতুনলাইনগুলি অন্তর্ভুক্ত করা। আপনি বিকল্প অপারেটরগুলি %বা %%, যেমন তারা এই ধরণের কাজটি পরিচালনা করার জন্য তৈরি করেছেন তাও ব্যবহার করতে চাইতে পারেন :

grammar Parser {
    token TOP       { 
                      <headerRow>     \n
                      <valueRow>+ %%  \n
                    }
    token headerRow { <.ws>* %% <header> }
    token valueRow  { <.ws>* %% <value>  }
    token header    { \S+ }
    token value     { \S+ }
    token ws        { \h* }
} 

এর ফলাফল Parser.parse($dat)নিম্নলিখিত:

「ID     Name    Email
   1   test    test@email.com
 321   stan    stan@nowhere.net
」
 headerRow => 「ID     Name    Email」
  header => 「ID」
  header => 「Name」
  header => 「Email」
 valueRow => 「   1   test    test@email.com」
  value => 「1」
  value => 「test」
  value => 「test@email.com」
 valueRow => 「 321   stan    stan@nowhere.net」
  value => 「321」
  value => 「stan」
  value => 「stan@nowhere.net」
 valueRow => 「」

যা আমাদের দেখায় যে ব্যাকরণ সফলভাবে সমস্ত কিছু পার্স করেছে। যাইহোক, আসুন আপনার প্রশ্নের দ্বিতীয় অংশটির দিকে মনোনিবেশ করুন, আপনি এটি চান যে এটি আপনার জন্য একটি ভেরিয়েবলে পাওয়া যায়। এটি করার জন্য, আপনাকে একটি ক্রিয়াকলাপ সরবরাহ করতে হবে যা এই প্রকল্পের জন্য খুব সহজ। আপনি কেবল এমন একটি শ্রেণী তৈরি করেছেন যার পদ্ধতিগুলি আপনার ব্যাকরণের পদ্ধতির সাথে মেলে (যদিও খুব সাধারণ বিষয়গুলি যেমন value/ headerযেগুলিকে স্ট্রিংফিকেশন ছাড়াও বিশেষ প্রক্রিয়াকরণের প্রয়োজন হয় না তবে এড়ানো যায়)। আপনার প্রসেসিং পরিচালনা করার জন্য আরও কিছু সৃজনশীল / কমপ্যাক্ট উপায় রয়েছে, তবে আমি উদাহরণের জন্য মোটামুটি প্রাথমিক পদ্ধতিতে যাব। এখানে আমাদের ক্লাস:

class ParserActions {
  method headerRow ($/) { ... }
  method valueRow  ($/) { ... }
  method TOP       ($/) { ... }
}

প্রতিটি পদ্ধতিতে স্বাক্ষর থাকে ($/)যা হ'ল রেগেক্স ম্যাচ ভেরিয়েবল। সুতরাং, এখন প্রতিটি টোকেন থেকে আমরা কী তথ্য চাই জিজ্ঞাসা করা যাক। শিরোলেখের সারিতে, আমরা প্রতিটি একক শিরোনামের মানটি চাই want তাই:

  method headerRow ($/) { 
    my   @headers = $<header>.map: *.Str
    make @headers;
  }

এটি একটি কোয়ান্টিফায়ার সঙ্গে কোন টোকেন হিসেবে গণ্য করা হবে Positional, তাই আমরা কিছু সঙ্গে প্রতিটি পৃথক হেডার ম্যাচের অ্যাক্সেস করতে পারে $<header>[0], $<header>[1],, ইত্যাদি কিন্তু ম্যাচ বস্তু তাই আমরা দ্রুত তাদের stringify। makeকমান্ড অন্যান্য টোকেন এই বিশেষ ডেটা রয়েছে যা আমরা তৈরি করেছি অ্যাক্সেসের অনুমতি দেয়।

আমাদের মান সারিটি অভিন্নরূপে দেখবে, কারণ $<value>টোকেনগুলি আমাদের যা যত্ন করে।

  method valueRow ($/) { 
    my   @values = $<value>.map: *.Str
    make @values;
  }

আমরা শেষ পদ্ধতিতে পৌঁছে গেলে, আমরা হ্যাশ দিয়ে অ্যারে তৈরি করতে চাই।

  method TOP ($/) {
    my @entries;
    my @headers = $<headerRow>.made;
    my @rows    = $<valueRow>.map: *.made;

    for @rows -> @values {
      my %entry = flat @headers Z @values;
      @entries.push: %entry;
    }

    make @entries;
  }

এখানে আপনি দেখতে পাচ্ছেন যে আমরা যে প্রক্রিয়াতে প্রক্রিয়াকরণ করেছি তাতে কীভাবে অ্যাক্সেস করতে পারি headerRow()এবং valueRow(): আপনি .madeপদ্ধতিটি ব্যবহার করেন । যেহেতু একাধিক ভ্যালুআর রয়েছে, তাদের প্রতিটি madeমান পেতে আমাদের একটি মানচিত্র তৈরি করতে হবে (এটি এমন একটি পরিস্থিতি যেখানে আমি আমার ব্যাকরণটি কেবল ব্যাকরণে লেখার ঝোঁক রাখি <header><data>এবং একাধিক সারি হিসাবে ডেটাটিকে পরাস্ত করি, তবে এটি হ'ল যথেষ্ট সহজ এটি খুব খারাপ নয়)।

এখন যেহেতু আমাদের দুটি শিরোনামে শিরোনাম এবং সারি রয়েছে, কেবল এগুলি হ্যাশগুলির একটি অ্যারে তৈরি করার বিষয়, যা আমরা লুপটিতে করি forflat @x Z @yশুধু উপাদানের intercolates এবং হ্যাশ নিয়োগ আমরা কি মানে, কিন্তু অন্য কোন উপায়ে হ্যাশ অ্যারের যদি আপনি চান পেতে হয়।

একবার আপনি হয়ে গেলে, আপনি কেবল makeএটি করেন, এবং তারপরে এটি madeপার্সে পাওয়া যাবে:

say Parser.parse($dat, :actions(ParserActions)).made
-> [{Email => test@email.com, ID => 1, Name => test} {Email => stan@nowhere.net, ID => 321, Name => stan} {}]

এগুলি কোনও পদ্ধতিতে মোড়ানো মোটামুটি সাধারণ

sub parse-tsv($tsv) {
  return Parser.parse($tsv, :actions(ParserActions)).made
}

এইভাবে আপনি বলতে পারেন

my @entries = parse-tsv($dat);
say @entries[0]<Name>;    # test
say @entries[1]<Email>;   # stan@nowhere.net

আমি মনে করি আমি ক্রিয়া শ্রেণিটি অন্যরকম লিখব। class Actions { has @!header; method headerRow ($/) { @!header = @<header>.map(~*); make @!header.List; }; method valueRow ($/) {make (@!header Z=> @<value>.map: ~*).Map}; method TOP ($/) { make @<valueRow>.map(*.made).List }অবশ্যই আপনাকে প্রথমে এটি ইনস্ট্যান্ট করতে হবে :actions(Actions.new)
ব্র্যাড গিলবার্ট

@ ব্র্যাডগিলবার হ্যাঁ, তাত্পর্য এড়ানোর জন্য আমি আমার ক্রিয়াকলাপগুলি লেখার প্রবণতা রাখি, তবে যদি class Actions { has @!header; has %!entries … }তাড়াতাড়ি করা হয় তবে আমি সম্ভবত মূল্য দিয়েছি এবং সরাসরি মূল্যগুলি দিয়েছি যাতে আপনি কেবল শেষ করতে পারেন method TOP ($!) { make %!entries }। তবে এটি সর্বোপরি রাকু এবং টিমটিউটিডিআই :-)
ব্যবহারকারী 0721090601

এই তথ্যটি পড়ার মাধ্যমে ( ডকস.আরাকু.আর.আর.গ্ল্যাংওয়েজ / রিজেক্সেস_মডিফায়েড_কোন্টিফায়ার :_১০,২০% ), আমি মনে করি যে আমি বুঝতে পেরেছি ( নিউলাইন দ্বারা সীমাগুলি সজ্জিত করা<valueRow>+ %% \n ক্যাপচার করুন) তবে এই যুক্তিটি অনুসরণ করা <.ws>* %% <header>হবে "ক্যাপচার alচ্ছিক হোয়াইটস্পেস যা অ-শ্বেত স্পেস দ্বারা সীমিত করা হয় "। আমি কিছু অনুপস্থিত করছি?
ক্রিস্টোফার বটমস

টুইটারে <.ws>ক্যাপচার না ( <ws>হবে)। ওপিতে উল্লেখ করা হয়েছে যে টিএসভি ফর্ম্যাটটি .চ্ছিক সাদা স্থানের সাথে শুরু হতে পারে। বাস্তবে, এটি সম্ভবত আরও ভালভাবে সংজ্ঞায়িত লাইন-স্পেসিং টোকেনের সাথে সংজ্ঞায়িত করা হবে \h*\n\h*, যা মানটির জন্য রা যুক্তিকে আরও যুক্তিযুক্তভাবে সংজ্ঞায়িত করার অনুমতি দেবে<header> % <.ws>
user0721090601

@ user0721090601 আমি পড়ার কথা মনে করি না %/ এর %%আগে "বিকল্প" বিকল্প হিসাবে অভিহিত করেছি। তবে এটি সঠিক নাম। (যেহেতু এটির জন্য ব্যবহার |, ||এবং কাজিন্স বোনরা আমাকে সর্বদা অদ্ভুত বলে আঘাত করেছে has) আমি এই "পিছনে" কৌশলটি আগে ভাবিনি। তবে এটি কেবলমাত্র প্যাটার্নের ম্যাচগুলির মধ্যেই নয় বরং পৃথক উভয় প্রান্তে (ব্যবহার করা %%), বা শুরুতে নয়, শেষ হিসাবে (ব্যবহার করা %) মঞ্জুরি দিয়ে কিছু বিভাজক দৃ with়তার সাথে পুনরাবৃত্ত প্যাটার্নের সাথে মিলে যাওয়া রেজিক্সগুলি লেখার পক্ষে একটি দুর্দান্ত প্রতিভা , শেষে শেষে বিকল্প কিন্তু ruleএবং যুক্তি শুরু না :s। খুশী হলাম। :)
রায়ফ

11

টিএল; ডিআর: আপনি না কেবল ব্যবহার করুন Text::CSV, যা প্রতিটি বিন্যাসে ডিল করতে সক্ষম।

আমি দেখাব যে বয়স কত Text::CSVসম্ভবত কার্যকর হবে:

use Text::CSV;

my $text = q:to/EOF/;
ID  Name    Email
   1    test    test@email.com
 321    stan    stan@nowhere.net
EOF
my @data = $text.lines.map: *.split(/\t/).list;

say @data.perl;

my $csv = csv( in => @data, key => "ID");

print $csv.perl;

এখানে মূল অংশটি হ'ল ডেটা মংগিং যা প্রাথমিক ফাইলটিকে অ্যারে বা অ্যারে (ইন @data) এ রূপান্তর করে । এটি কেবল প্রয়োজন, কারণ csvকমান্ডটি স্ট্রিংগুলি মোকাবেলা করতে সক্ষম নয়; যদি ডেটা কোনও ফাইলে থাকে তবে আপনি যেতে ভাল।

শেষ লাইনটি মুদ্রণ করবে:

${"   1" => ${:Email("test\@email.com"), :ID("   1"), :Name("test")}, " 321" => ${:Email("stan\@nowhere.net"), :ID(" 321"), :Name("stan")}}%

আইডি ক্ষেত্র হ্যাশটির মূল চাবিকাঠি হয়ে উঠবে এবং পুরো জিনিসটি হ্যাশের একটি অ্যারে।


2
ব্যবহারিকতার কারণে উজ্জীবিত। যদিও আমি নিশ্চিত নই, যদি ওপি ব্যাকরণ (আমার উত্তরের পদ্ধতির) শেখার জন্য আরও লক্ষ্য রাখছে বা কেবল পার্স করার প্রয়োজন হয় (আপনার উত্তরটির পদ্ধতির)। উভয় ক্ষেত্রেই তার উচিত :-)
ইউজার 0721090601

2
একই কারণে উত্সাহিত। :) আমি ভেবেছিলাম যে ওপি হয়তো রেগেক্স শব্দার্থবিজ্ঞানের (তারা আমার উত্তর) পদে কী ভুল করেছে তা শিখার লক্ষ্য রেখেছিল, এটি কীভাবে সঠিকভাবে করা যায় তা শিখতে লক্ষ্য করে (আপনার উত্তর), বা কেবল পার্স করার প্রয়োজন হয়েছে (জেজে এর উত্তর) )। দলবদ্ধভাবে সম্পাদিত কর্ম. :)
রায়ফ

7

টিএল; ডিআর regex এর ব্যাকট্র্যাক। tokenনা। এজন্য আপনার প্যাটার্নটি মিলছে না। এই উত্তরটি ব্যাখ্যা করার উপর এবং কীভাবে আপনার ব্যাকরণকে তুচ্ছভাবে সংশোধন করতে হবে তার উপর দৃষ্টি নিবদ্ধ করে। তবে আপনার সম্ভবত এটি পুনর্লিখন করা উচিত, বা বিদ্যমান পার্সার ব্যবহার করা উচিত যা রাকু রেজেক্সেস সম্পর্কে শিখার চেয়ে আপনি কেবল টিএসভি পার্স করতে চাইলে আপনার অবশ্যই করা উচিত ।

একটি মৌলিক ভুল বোঝাবুঝি?

আমি মনে করি আমি রাকুতে রেগেক্সগুলি সম্পর্কে মৌলিক কিছু ভুল বুঝছি।

(যদি আপনি ইতিমধ্যে "রেজেক্সেস" শব্দটি অত্যন্ত অস্পষ্ট শব্দটি জানেন তবে এই বিভাগটি এড়িয়ে যাওয়া বিবেচনা করুন))

একটি মূল বিষয় যা আপনি ভুল বোঝাবুঝি করতে পারেন তা হ'ল "রেজেক্সেস" শব্দের অর্থ। এখানে লোকাল ধরে নেওয়া কয়েকটি জনপ্রিয় অর্থ রয়েছে:

  • সাধারণ নিয়মিত প্রকাশ।

  • পার্ল রেজেক্সেস।

  • পার্ল সামঞ্জস্যপূর্ণ নিয়মিত এক্সপ্রেশন (পিসিআরই)।

  • উপরের যেকোনটির মতো দেখতে এবং অনুরূপ কিছু করে "রেজেক্সেস" নামে পরিচিত পাঠ্য প্যাটার্নের মিল রয়েছে ions

এগুলির কোনও অর্থই একে অপরের সাথে সামঞ্জস্যপূর্ণ নয়।

যদিও পার্ল রেইগেক্সগুলি সিনটিক্যালি আনুষ্ঠানিক নিয়মিত অভিব্যক্তিগুলির একটি সুপারস্টেট, এগুলি অনেক উপায়ে উপকারী তবে প্যাথলজিকাল ব্যাকট্র্যাকিংয়ের পক্ষেও বেশি ঝুঁকিপূর্ণ ।

পার্লের সাথে সামঞ্জস্যপূর্ণ নিয়মিত এক্সপ্রেশনগুলি পার্লের সাথে এই দিক থেকে সামঞ্জস্যপূর্ণ যে তারা মূলত ১৯৯০ এর দশকের শেষের দিকে স্ট্যান্ডার্ড পার্ল রেজেক্সেসের মতই ছিল এবং এই অর্থে যে পার্ল পিসিআরই ইঞ্জিন সহ প্লাগেবল রেজেক্স ইঞ্জিনগুলিকে সমর্থন করে, পিসিআরই রিজেক্স সিনট্যাক্স স্ট্যান্ডার্ডের সাথে একরকম নয় পার্ল রেগেক্স 2020 সালে পার্ল দ্বারা ডিফল্টরূপে ব্যবহৃত হয়েছিল।

এবং "রেজেক্সেস" নামক পাঠ্য প্যাটার্নের মিলের অভিব্যক্তিগুলি সাধারণত একে অপরের মতো দেখতে লাগে এবং সমস্ত পাঠ্য মিল রয়েছে, সেখানে সিনট্যাক্সে কয়েক ডজন, সম্ভবত শত শত, এমনকি একই বাক্য বিন্যাসের জন্য শব্দার্থবিজ্ঞানেও রয়েছে।

রাকু পাঠ্য প্যাটার্নের সাথে মেলে এমন অভিব্যক্তিগুলিকে সাধারণত "নিয়ম" বা "রেজেক্সেস" বলা হয়। "রেজেক্সেস" শব্দটির ব্যবহারটি এই সত্যটি বোঝায় যে এগুলি কিছুটা অন্যান্য রেজেক্সের মতো দেখায় (যদিও সিনট্যাক্সটি পরিষ্কার হয়ে গেছে)। "বিধি" শব্দটি এই সত্যটি প্রকাশ করে যে তারা বৈশিষ্ট্য এবং সরঞ্জামগুলির বিস্তৃত সেটগুলির অংশ যা পার্সিং (এবং তার বাইরে) পর্যন্ত স্কেল করে।

দ্রুত সমাধান

"রেজেক্সেস" শব্দের উপরের মৌলিক দিকটি বাদ দিয়ে, আমি এখন আপনার "রেজেক্স" এর আচরণের মৌলিক দিকটিতে ফিরে যেতে পারি ।

যদি আমরা ঘোষককে tokenঘোষণাকারীর জন্য আপনার ব্যাকরণের তিনটি নিদর্শনগুলি স্যুইচ করে থাকি তবে regexআপনার ব্যাকরণটি যেমন আপনি ইচ্ছা তেমন কাজ করে:

grammar Parser {
    regex TOP       { <headerRow><valueRow>+ }
    regex headerRow { [\s*<header>]+\n }
    token header    { \S+ }
    regex valueRow  { [\s*<value>]+\n? }
    token value     { \S+ }
}

A tokenএবং a এর মধ্যে একমাত্র পার্থক্য regexহ'ল regexব্যাকট্র্যাকগুলি যেখানে একটি tokenহয় না। এভাবে:

say 'ab' ~~ regex { [ \s* a  ]+ b } # 「ab」
say 'ab' ~~ token { [ \s* a  ]+ b } # 「ab」
say 'ab' ~~ regex { [ \s* \S ]+ b } # 「ab」
say 'ab' ~~ token { [ \s* \S ]+ b } # Nil

শেষ প্যাটার্নের প্রক্রিয়া চলাকালীন (এটি হতে পারে এবং প্রায়শই তাকে "রেইজেক্স" বলা যেতে পারে, তবে যার প্রকৃত ঘোষক তা tokenনয় regex), পূর্ববর্তী লাইনে রেজেেক্স প্রসেসিংয়ের সময় যেমন সাময়িকভাবে সম্পন্ন হয়েছিল, তেমনি \Sগিলে 'b'ফেলবে। তবে, প্যাটার্নটি একটি হিসাবে ঘোষিত হওয়ার কারণে token, নিয়ম ইঞ্জিন (ওরফে "রেজেক্স ইঞ্জিন") ব্যাকট্র্যাক করে না , সুতরাং সামগ্রিক মিলটি ব্যর্থ হয়।

এটি আপনার ওপিতে যা চলছে।

সঠিক ফিক্স

ব্যাকট্র্যাকিং আচরণটি ধরে নেওয়া থেকে সাধারণভাবে আরও ভাল সমাধান হ'ল কারণ এটি দূষিতভাবে নির্মিত স্ট্রিংয়ের সাথে ম্যাচ করার সময় বা দুর্ঘটনাক্রমে দুর্ভাগ্যবশত চরিত্রের মিশ্রণের সাথে ব্যবহার করার সময় এটি ধীর এবং এমনকি বিপর্যয়করভাবে ধীর হতে পারে (প্রোগ্রামটি ঝুলন্ত থেকে আলাদা নয়)।

কখনও কখনও regexs উপযুক্ত। উদাহরণস্বরূপ, আপনি যদি এক-বন্ধ লিখতে থাকেন এবং একটি রেজেক্স কাজটি করে থাকে, তবে আপনি হয়ে গেছেন। সেটা ঠিক আছে. এটি সেই কারণের অংশ যা / ... /রাকুতে সিনট্যাক্স ঠিক যেমন একটি ব্যাকট্র্যাকিং প্যাটার্ন ঘোষণা করে regex। (তারপরে আপনি র‌্যাচটিংয়ে/ :r ... / স্যুইচ করতে চাইলে আবারও আপনি লিখতে পারেন - "র‌্যাচেট" এর অর্থ "ব্যাকট্র্যাক" এর বিপরীত, সুতরাং শব্দার্থে একটি রেজেক্স স্যুইচ করে )):rtoken

মাঝে মাঝে ব্যাকট্র্যাকিংয়ের বিশ্লেষণ প্রসঙ্গে একটি ভূমিকা রয়েছে। উদাহরণস্বরূপ, যখন raku জন্য ব্যাকরণ সাধারণভাবে backtracking না eschews, এবং পরিবর্তে শত শত হয়েছে rules এবং tokenগুলি, তবুও এটি এখনো 3 আছে regexসে।


আমি @ user0721090601 ++ এর উত্তরটি আপলোড করেছি কারণ এটি কার্যকর। এটি এমন বেশ কয়েকটি জিনিসকেও সম্বোধন করে যা অবিলম্বে আমার কাছে আপনার কোডটিতে অজ্ঞাতসারে বন্ধ হয়ে গেছে বলে মনে হয়েছিল এবং গুরুত্বপূর্ণভাবে, এটির জন্য আটকে আছে token। এটি আপনার পছন্দসই উত্তরটি হতে পারে যা দুর্দান্ত হবে।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.