Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

Best way to handle interactive user input?

by Ppeoc (Beadle)
on Nov 26, 2015 at 07:02 UTC ( [id://1148640]=perlquestion: print w/replies, xml ) Need Help??

Ppeoc has asked for the wisdom of the Perl Monks concerning the following question:

I have a huge XML file to parse. I was thinking that instead of parsing the whole file, I could ask the user to input a few options and then parse only that portion of the file. I was basically planning to do this
Enter option to be parsed 1. Fiction 2. History 3. Religion 10. Non fiction

So once the user enters the number 2, Book 2 will get selected and bunch of other options will be displayed as follows

Selected Book 2. Options are as follow 1. World History 2. American History 3. Oriental History 10. Indian History

Each option is nested differently with different levels. The plan is to use a switch statement on the returned $_ to display options for the next level. How do I navigate to a different part of the program and display options according to the genre selected. Thanks!

use strict; use warnings; use Switch; my $level1; print "Select options: \n 1 Fiction \n 2 History \n 3 Religion \n"; my $no = getIP('Enter a digit : ', /^\d/); switch ($no) { case 1 { $level1 = ?? } case 2 { $level1 = ?? } case 3 { $level1 = ?? } else { $level1= ?? } } print $level1; sub getIP { print $_[0]; do { $_ = <STDIN>; chomp; } while ($_[1] && $_ !~ $_[1]); return $_; }

Replies are listed 'Best First'.
Re: Best way to handle interactive user input?
by kcott (Archbishop) on Nov 26, 2015 at 10:27 UTC

    G'day Ppeoc,

    I started to write an XML::LibXML solution but got distracted (with real world stuff). In the meantime, you've updated your post and have possibly settled on an XML::Twig solution.

    [Aside: Please don't update your posts without indicating what's changed - see "How do I change/delete my post?" for more details on this.]

    Anyway, the code's written, and seems to do the type of thing you're after, so here it is. It is intended to be a guide only — you'll need to adapt it for your specific needs.

    You gave no indication of the XML you're working with. I've dummied up 'pm_1148640_xml_parse.xml' for demonstration purposes:

    <library> <category> <name>Fiction</name> <book> <title>Sci-Fi</title> <title>Detective</title> </book> </category> <category> <name>History</name> <book> <title>Modern</title> <title>Ancient</title> </book> </category> </library>

    Here's the code (pm_1148640_xml_parse.pl):

    #!/usr/bin/env perl use strict; use warnings; use XML::LibXML; my $parser = XML::LibXML::->new(); my $doc = $parser->load_xml(location => 'pm_1148640_xml_parse.xml'); get_category($doc); sub get_category { my ($doc) = @_; my @categories = $doc->findnodes('/library/category/name/text()'); push @categories, 'Exit'; my $selection = 0; print "Select a book category:\n"; print ++$selection, ". $_\n" for @categories; my $choice = get_user_selection({ map { $_ => undef } 1 .. $select +ion}); return if $choice == $selection; get_book($doc, $categories[$choice - 1]); } sub get_book { my ($doc, $category) = @_; my $xpath = "/library/category[name=\"$category\"]/book/title/text +()"; my @books = $doc->findnodes($xpath); push @books, 'Back to Categories'; my $selection = 0; print "Select a $category book:\n"; print ++$selection, ". $_\n" for @books; my $choice = get_user_selection({ map { $_ => undef } 1 .. $select +ion}); if ($choice == $selection) { get_category($doc); return; } print "You selected the $category book: '$books[$choice - 1]'\n"; } sub get_user_selection { my $valid_selection = shift; my $selection = ''; while (1) { print 'Enter selection: '; chomp($selection = scalar <>); last if exists $valid_selection->{$selection}; print "Invalid selection [$selection]\n"; } return $selection; }

    And here's an example run:

    $ pm_1148640_xml_parse.pl Select a book category: 1. Fiction 2. History 3. Exit Enter selection: 9 Invalid selection [9] Enter selection: 1 Select a Fiction book: 1. Sci-Fi 2. Detective 3. Back to Categories Enter selection: 3 Select a book category: 1. Fiction 2. History 3. Exit Enter selection: 2 Select a History book: 1. Modern 2. Ancient 3. Back to Categories Enter selection: 2 You selected the History book: 'Ancient'

    — Ken

      Ken, Thank you for your help. I am open to using any XML module for parsing my documents. I have parsed my document using XML::Parser, XML::Twig and XML::XPath. Since each of these XML files are huge, I would like to try out a couple of parsing methods to find the most efficient. And your code is a great help for this. Need to figure out how to use to parse my document which has different levels of nested children. Thank you :)
Re: Best way to handle interactive user input?
by Discipulus (Canon) on Nov 26, 2015 at 08:13 UTC
    hello Ppeoc,

    your question is not very clear, at least to me: XML.. menu.. and the code you put at the bottom.
    If I understand you vaguely describe a problem like: i have an XML file, i want let the user to navigate some menu based on the content of the XML file.

    If so you have some billion of different possibility to do it in Perl. It depends on your skills and your taste. I would do something like:

    1-choose a good XML parser: i always suggest XML::Twig but there are others (never fall in the XML::Simple pitfall)

    2-parse the XML and build up a nested datastructure (an HashOfHashes) with a leaf for each menu you want displyed, like
    #PSEUDO EXAMPLE menu_mustsee_movies => { descr=>'must see movies', previous=>'menu_good_movies', entries=>\@data_from_xml, }
    Then 3-choose your user interface, a GUI like Tk or simply Term::Readline

    4-build up a loop where you present the top_level_menu, grab the input and display the choosed menu.

    If you interested there is a bit aged but working Term::Menu module and also see my own example of Term::Readline in response to my own question about Term::ReadLine and it's imported function

    HtH
    L*
    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
      My XML parser code
      my $twig = XML::Twig->new( twig_handlers => { 'Mughals' => sub { + my ($twig, $el) = @_; + $twig->purge; + }, 'Mughals//*' => sub { + my $a = $_->tag; + if ($a eq "Book") + { + print $fout1 "\n"; + print $fout2 "\n"; + } #print $fout1 $_->tag, ", ", $_->text, unless ($_->has_ +children('#ELT')); print $fout2 $_->tag, ",", unless ($_->has_children('#E +LT')); print $fout1 $_->text, ",", unless ($_->has_children('# +ELT')); } } ); $twig->parsefile('book.xml');
      This is a part of the code for parsing one part of the file 'Mughals'. The path to Mughals is History->Indian History -> Mughals. I am confused about how to accept this input from the user.
      Hey! Sorry about being so vague. I hope this makes more sense. So I have used XML::Twig to parse the whole file and it took 70 secs for one file. And I have to parse many such files at one go. So instead I thought I could just display the options to parse and parse only that particular section. But like you said, it may be a good idea to display leaf for each menu and then do step 3 and 4.
        if you have big big files you can enjoy the ability of XML::Twig to parse on the fly using twig_handler if you are smart enough you can build an handler that does everything you need as grab data and display the menu (the twig knows his father and his silbings and his children too). That said i was no able to navigate interactively an XML when i tried to do so. It would be simpler to put all the data in minimal database (SQLite)

        L*
        There are no rules, there are no thumbs..
        Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
Re: Best way to handle interactive user input?
by Jenda (Abbot) on Nov 26, 2015 at 23:22 UTC

    XML is a passable data transfer format, but an absolutely insane data storage format. Parse the data once, stuff it in a database (DBD::Sqlite is enough) and then query the database whenever you need any of the data.

    Jenda
    Enoch was right!
    Enjoy the last years of Rome.

Re: Best way to handle interactive user input?
by Anonymous Monk on Nov 26, 2015 at 08:01 UTC

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1148640]
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others chanting in the Monastery: (2)
As of 2024-04-26 00:57 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found