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

Hi, im having a File with semicolon(;) seperated entries

For Example

ABC;123;;;;;HELLO;

DEF;345;;BANANA;12DEF;44,55;4*12;;;;;;;;3;

and what i now need is a way to add quote around everything exept the first entry per Line

ABC;"123";"";"";"";"";"HELLO";

DEF;"345";"";"BANANA";"12DEF";"44,55";"4*12";"";"";"";"";"";"";"";"3";

Each line can have a different amount of entries.

So basicly i have to look into the file in a random folder add the quotes around everything.

After that i have to copy the modified file into a new folder.

How could a achive this?

Replies are listed 'Best First'.
Re: Add Quotes to entries in a file
by choroba (Cardinal) on Jul 12, 2018 at 13:12 UTC
    Piece of cake!
    perl -pe 's/;/";"/g; s/";"/;"/; s/"$//' input > output

    The first substitution wraps each semicolon in double quotes. The second one removes the double quotes before the first semicolon, the third one removes the last double quote.

    To copy the file, you can also use Path::Tiny's copy or File::Copy's copy or cp.

    ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
      Delightfully clever, turning the statement of the problem on its head to create an elegant solution.
Re: Add Quotes to entries in a file
by Tux (Canon) on Jul 12, 2018 at 15:27 UTC

    But, but, but, what if any of those fields is already quoted? And what if that field contains a ;?

    use Text::CSV_XS "csv"; csv (in => csv (in => *DATA, sep => ";"), sep => ";", quote_always => +1, out => \my $out); print $out =~ s/^"([^"]*)"/$1/grm; __END__ ABC;123;;;;;HELLO; DEF;345;;BANANA;12DEF;44,55;4*12;;;;;;;;3;

    -->

    $ perl test.pl ABC;"123";"";"";"";"";"HELLO";"" DEF;"345";"";"BANANA";"12DEF";"44,55";"4*12";"";"";"";"";"";"";"";"3"; +""

    Enjoy, Have FUN! H.Merijn
      ABC;"123";"";"";"";"";"HELLO";""

      niseus does not appear to want (what at first glance one would expect to be) the empty field at the end of each of the OPed example records; the semicolon seems to serve as both a field delimiter and as a record delimiter. Just to keep things simple. I'm not sure how to handle that with straight Text::CSV; my guess is the final semicolon needs separate operations to deal with it.


      Give a man a fish:  <%-{-{-{-<

Re: Add Quotes to entries in a file
by hippo (Archbishop) on Jul 12, 2018 at 13:13 UTC

    It's an unusual requirement for sure (homework?) but taking you at your word:

    use strict; use warnings; use Test::More tests => 2; my @rec = ( { have => 'ABC;123;;;;;HELLO;', want => 'ABC;"123";"";"";"";"";"HELLO";' }, { have => 'DEF;345;;BANANA;12DEF;44,55;4*12;;;;;;;;3;', want => 'DEF;"345";"";"BANANA";"12DEF";"44,55";"4*12";"";"";"" +;"";"";"";"";"3";' } ); for my $this (@rec) { $this->{have} =~ s/;/";"/g; $this->{have} =~ s/";/;/; $this->{have} =~ s/;"$/;/; is ($this->{have}, $this->{want}) }

    I have a sneaking suspicion that the goalposts may be about to move. Let's hope not.

Re: Add Quotes to entries in a file
by johngg (Canon) on Jul 12, 2018 at 18:05 UTC

    A solution using split, a single substitution on an array slice then a join.

    johngg@abouriou ~/perl/Monks $ perl -Mstrict -Mwarnings -E ' open my $inFH, q{<}, \ <<__EOD__ or die $!; ABC;123;;;;;HELLO; DEF;345;;BANANA;12DEF;44,55;4*12;;;;;;;;3; __EOD__ my $outFile = do { \ my $dummy; }; open my $outFH, q{>}, $outFile or die $!; while ( <$inFH> ) { my @items = split m{;}; s{(.*)}{"$1"} for @items[ 1 .. ( $#items - 1 ) ]; print $outFH join q{;}, @items; } print $$outFile;' ABC;"123";"";"";"";"";"HELLO"; DEF;"345";"";"BANANA";"12DEF";"44,55";"4*12";"";"";"";"";"";"";"";"3";

    I hope this is of interest.

    Cheers,

    JohnGG

      No substitution is necessary since you're just wrapping double-quotes around strings. Also, in the pure-string version, a split LIMIT of -1 is needed to capture a final empty | empty string field for later re-join-ing; in the file-based version, the final field is a newline, which gets very neatly join-ed back on.

      c:\@Work\Perl\monks>perl -wMstrict -le "my @strings = ( 'ABC;123;;;;;HELLO;', 'DEF;345;;BANANA;12DEF;44,55;4*12;;;;;;;;3;', ); ;; for my $s (@strings) { printf qq{'$s' \n>}; my @items = split m{;}, $s, -1; $_ = qq{\"$_\"} for @items[ 1 .. ( $#items - 1 ) ]; print join(q{;}, @items), qq{< \n}; } " 'ABC;123;;;;;HELLO;' >ABC;"123";"";"";"";"";"HELLO";< 'DEF;345;;BANANA;12DEF;44,55;4*12;;;;;;;;3;' >DEF;"345";"";"BANANA";"12DEF";"44,55";"4*12";"";"";"";"";"";"";"";"3" +;<


      Give a man a fish:  <%-{-{-{-<

Re: Add Quotes to entries in a file
by AnomalousMonk (Archbishop) on Jul 12, 2018 at 15:09 UTC

    FWIW, a single-regex solution:

    c:\@Work\Perl\monks>perl -wMstrict -le "use Test::More 'no_plan'; use Test::NoWarnings; ;; my @vectors = ( [ 'ABC;123;;;;;HELLO;' => q{ABC;'123';'';'';'';'';'HELLO';} ], [ 'DEF;345;;BANANA;12DEF;44,55;4*12;;;;;;;;3;' => q{DEF;'345';'';'BANANA';'12DEF';'44,55';'4*12';'';'';'';'';'';''; +'';'3';} ], ); ;; for my $ar_vector (@vectors) { my ($string, $expected) = @$ar_vector; (my $got = $string) =~ s{ (?<= ;) ([^;]*) (?= ;) }{'$1'}xmsg; is $got, $expected, qq{'$string' -> \n >$expected<}; } " ok 1 - 'ABC;123;;;;;HELLO;' -> # >ABC;'123';'';'';'';'';'HELLO';< ok 2 - 'DEF;345;;BANANA;12DEF;44,55;4*12;;;;;;;;3;' -> # >DEF;'345';'';'BANANA';'12DEF';'44,55';'4*12';'';'';'';'';'';'';''; +'3';< ok 3 - no warnings 1..3
    Notes:
    • Run under Win7, Perl version 5.8.9.
    • Single-quotes were used in the example code instead of doubles because Windoze command-line escaping would have made the cut-paste unreadable. Substitute double-quotes for single-quotes in the replacement of the substitution to address original problem.
    • The  /r modifier for a  s/// substitution added with Perl version 5.14 makes the code slightly simpler (tested):
          my $got = $string =~ s{ (?<= ;) ([^;]*) (?= ;) }{'$1'}xmsgr;
      (See  s/// in perlop.)

    Update: The regex used above has also been tested and works not only with "pure" strings (i.e., with nothing after the final semicolon), but with test strings ending in a newline after the final semicolon, as would be presumed to be the case for strings read from a file.


    Give a man a fish:  <%-{-{-{-<

Re: Add Quotes to entries in a file
by jwkrahn (Abbot) on Jul 12, 2018 at 18:38 UTC
    $ echo "ABC;123;;;;;HELLO; DEF;345;;BANANA;12DEF;44,55;4*12;;;;;;;;3;" | perl -lpe's/;\K([^;]*)/" +$1"/g' ABC;"123";"";"";"";"";"HELLO";"" DEF;"345";"";"BANANA";"12DEF";"44,55";"4*12";"";"";"";"";"";"";"";"3"; +""

      This also produces an extraneous "" field at the end of the string that niseus seems not to want; see this. The regex just needs another look-ahead to fix the problem (if it's actually a problem); see this.


      Give a man a fish:  <%-{-{-{-<

Re: Add Quotes to entries in a file
by niseus (Initiate) on Jul 12, 2018 at 14:11 UTC
    @chobra

    when i try your line i just get Can't find string terminator "'" anywhere before EOF at -e line 1. Im probally doing something wrong but im just beginning with pearl so bear with me :)

    @hippo

    Would that work if i had different files with different content?

      Would that work if i had different files with different content?

      niseus: That's exactly why hippo presented his solution in the Test::More format: so that you can add all the extra test cases (content) you feel you need (and you're encouraged to add many). See How to ask better questions using Test::More and sample data. The Test::More module family is very useful in general development. Testing with different files I leave to you to work out.


      Give a man a fish:  <%-{-{-{-<

      Are you on MSWin? Quoting is different there, single quotes don't work. Have you used copy/paste or retyped the command? Also note that @name doesn't work on PerlMonks. To get the author alerted about a reply, reply to their node.

      ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
        Hi, i now did it with " instead if ' but now all it does is copy my File in a new one but no quotes are added.
      when i try your line i just get Can't find string terminator "'" anywhere before EOF at -e line 1. Im probally doing something wrong but im just beginning with pearl so bear with me :)

      If you are using Losedows you will need to change the surrounding single quotes to double quotes and escape all the double quotes in the Perl. You may find it easier to put the solution in a file. One liners in Losedows are more awkward than in *u*x.

      Regards,

      John Davies

      Would that work if i had different files with different content?

      Yes, but only if the different content follows the same pattern of course. Feel free to expand @rec with your extra data sets and see.