tags:

views:

577

answers:

8

If I declared a variable $myString with the value '3 ' (notice the white space). Is there any function to remove the white space for the return value. A little like SomeFun($myString) then return '3' (without white space).

#!C:\Perl\bin\perl.exe
use strict;
use warnings;
use Data::Dumper; 

my $fh = \*DATA; 

print Dumper parse_constant_spec( $fh ); 


# Parse a constant spec file. 
# Pass in a handle to process. 
# As long as it acts like a file handle, it will work. 
sub parse_constant_spec { 
    my $fh = shift; 

    my %spec; 

    # Until file is done: 
        # Read in a whole block 
    while( my $block = read_block($fh) ) { 

        # Parse the and return key/value pairs for a hash. 
        my %constant = parse_block( $block ); 

        # Store a ref to the hash in a big hash of all blocks, keyed by constant_name. 
        $spec{ $constant{const_name} } = \%constant; 

    } 

    # Return ref to big hash with all block data 
    return \%spec; 
} 

# Read a constant definition block from a file handle. 
# void return when there is no data left in the file. 
# Otherwise return an array ref containing lines to in the block.  
sub read_block { 
    my $fh = shift; 

    my @lines; 
    my $block_started = 0; 

    while( my $line = <$fh> ) { 

        $block_started++ if $line =~ /^constant/; 

        if( $block_started ) { 

            last if $line =~ /^\s*$/; 

            push @lines, $line; 
        } 
    } 

    return \@lines if @lines; 

    return; 
} 


sub parse_block { 
    my $block = shift; 
    my ($start_line, @attribs) = @$block; 

    my %constant; 

    # Break down first line: 
    # First separate assignment from option list. 
    my ($start_head, $start_tail) = split /=/, $start_line; 

    # work on option list 
    my @options = split /\s+/, $start_head; 

    # Recover constant_name from options: 
    $constant{const_name} = pop @options; 
    $constant{options} = \@options; 

    # Now we parse the value/type specifier 
    @constant{'type', 'value' } = parse_type_value_specifier( $start_tail ); 

    # Parse attribute lines. 
    # since we've already got multiple per line, get them all at once. 
    chomp @attribs; 
    my $attribs = join ' ', @attribs; 

    #  we have one long line of mixed key = "value" or key = <TYPE VALUE>  

    @attribs = $attribs =~ /\s*(\w+\s+=\s+\w+\s+|\w+\s+=\s+".*?"|\w+\s+=\s+<.*?>)\s*/g; 

    for my $attrib ( @attribs ) { 
        warn "$attrib\n"; 
        my ($name, $value) = split /\s*=\s*/, $attrib; 

        if( $value =~ /^"/ ) {  
            $value =~ s/^"|"\s*$//g; 
        } 
        elsif( $value =~ /^</ ) { 
           $value = [ parse_type_value_specifier( $start_tail ) ]; 
        } 
        else { 
            warn "Bad line"; 
        } 

        $constant{ $name } = $value; 
    } 

    return %constant; 
} 

sub parse_type_value_specifier { 
    my $tvs = shift; 

    my ($type, $value) = $tvs =~ /<(\w+)\s+(.*?)>/; 

    return $type, $value; 
} 

__DATA__ 
constant fixup GemEstabCommDelay = <U2 20> 
    vid = 6 
    name = "ESTABLISHCOMMUNICATIONSTIMEOUT" 
    units = "s" 
    min = <U2 0> 
    max = <U2 1800> 
    default = <U2 20> 


constant fixup private GemConstantFileName = <A "C:\\TMP\\CONST.LOG"> 
    vid = 4 
    name = ""  units = "" 


constant fixup private GemAlarmFileName = <A "C:\\TMP\\ALARM.LOG"> 
    vid = 0 
    name = "" 
    units = ""   

Output:

D:\learning\perl>hello1.pl
vid = 6
Bad line at D:\learning\perl\hello1.pl line 102, <DATA> line 8.
name = "ESTABLISHCOMMUNICATIONSTIMEOUT"
units = "s"
min = <U2 0>
max = <U2 1800>
default = <U2 20>
vid = 4
Bad line at D:\learning\perl\hello1.pl line 102, <DATA> line 13.
name = ""
units = ""
vid = 0
Bad line at D:\learning\perl\hello1.pl line 102, <DATA> line 18.
name = ""
units = ""
$VAR1 = {
          'GemAlarmFileName' => {
                                  'vid' => '0      ',
                                  'options' => [
                                                 'constant',
                                                 'fixup',
                                                 'private'
                                               ],
                                  'value' => '"C:\\\\TMP\\\\ALARM.LOG"',
                                  'name' => '',
                                  'type' => 'A',
                                  'const_name' => 'GemAlarmFileName',
                                  'units' => ''
                                },
          'GemEstabCommDelay' => {
                                   'vid' => '6      ',
                                   'options' => [
                                                  'constant',
                                                  'fixup'
                                                ],
                                   'value' => '20',
                                   'min' => [
                                              'U2',
                                              '20'
                                            ],
                                   'name' => 'ESTABLISHCOMMUNICATIONSTIMEOUT',
                                   'max' => [
                                              'U2',
                                              '20'
                                            ],
                                   'default' => [
                                                  'U2',
                                                  '20'
                                                ],
                                   'type' => 'U2',
                                   'units' => 's',
                                   'const_name' => 'GemEstabCommDelay'
                                 },
          'GemConstantFileName' => {
                                     'vid' => '4      ',
                                     'options' => [
                                                    'constant',
                                                    'fixup',
                                                    'private'
                                                  ],
                                     'value' => '"C:\\\\TMP\\\\CONST.LOG"',
                                     'name' => '',
                                     'type' => 'A',
                                     'const_name' => 'GemConstantFileName',
                                     'units' => ''
                                   }
        };

D:\learning\perl>

You could notice that 'vid' => '0 ', (notice the white space)

The code above from the answer. I am studying it. :-)

Thank you.

+5  A: 
$myString =~ s/^\s*(.*)\s*$/$1/;

This will trim whitespace from both sides.

from just the right:

$myString =~ s/\s*$//;
Alex JL
Hi Salsa. It works well. Followed your guide, `$value =~ s/\s*$//;` then call `print DEST_XML_FILE "\"$value\"";`
Nano HE
I had trouble with this.. I think it would be better to use a non greedy capture instead: (.*?)
Roman Stolper
+3  A: 
sub trim($)
{
    my $string = shift;
    $string =~ s/^\s+//;
    $string =~ s/\s+$//;
    return $string;
}


print trim($myString)
GxG
Hi GxG. I tested with your code. `print DEST_XML_FILE trim("\"$value\"");` works well. otherwise `print DEST_XML_FILE trim("$value");` can't work. Would you please help me find why? I need use the 1st expression to add doublequote between the variable.
Nano HE
\" represents an escape sequence so if you want to put quotes you need to put \" otherwise it will be seen as a simple quoted text and will appear without the quotes
GxG
Hi GxG. it works when i declared another tempString. `my $tempString = trim($value);` then call `print DEST_XML_FILE "\"$tempString\"";` Thank you.
Nano HE
My pleasure... good luck
GxG
+3  A: 

Try this:

# Delete leading/trailing whitespace.
$string =~ s/^\s+|\s+$//g;
John Feminella
+1. It works too. thank you.
Nano HE
+2  A: 

If your white space is just spaces, then the following code will remove all spaces:

$mystring =~ tr/ //ds;
Chip Uni
+1 . It works well. But i feel your script syntax is a little strange and it is hard to understand based on my knowledge :-)
Nano HE
+1  A: 
sub trim
{
    my $str = $_[0];
    $str=~s/^\s+|\s+$//g;
    return $str;
}

print trim(" 4 ");
ghostdog74
Hi ghostdog74. I tested with your code. `print DEST_XML_FILE trim("\"$value\"");` works well. otherwise `print DEST_XML_FILE trim("$value");` can't work. Would you please help me find why? I need use the 1st expression to add doublequote between the variable.
Nano HE
It's actually faster to remove the leading and trailing whitespace in separate substitutions. That alternation screws it all up. :)
brian d foy
@Nano, the alternation works as expected. trimming ending and leading spaces. that's all it does. You should do whatever you want to $value before passing it to trim() function. Otherwise, show an example of what $value is, and then describe what you want to see after trimming.
ghostdog74
@ghostdog74. I updated the whole script and it's output. Would you please do more dissection about the $value. Thank you. :-)
Nano HE
@ghostdog74. it works when i declared another tempString. `my $tempString = trim($value);` then call `print DEST_XML_FILE "\"$tempString\"";`
Nano HE
+4  A: 

Another potential alternative solution is Text::Trim from CPAN, which will "remove leading and/or trailing whitespace from strings". It has a trim function which may suit your needs.

toolic
I've got a bookmark, and I'll read it when I get a chance. Thanks.
Nano HE
+1  A: 

Hello Nano HE

Just looking over your program, I found 3 spots that could be improved or fixed.

I apologize if my code doesn't format well. :-(

In your function parse_block(...), there are 3 items that need attention.

@attribs = $attribs =~ /\s*(\w+\s+=\s+\w+\s+|\w+\s+=\s+".*?"|\w+\s+=\s+<.*?>)\s*/g;

To eliminate the white space after vid => '6 ', just don't include the \s+ at the end of your first sub-regex.

Write it as:

@attribs = $attribs =~ /\s*(\w+\s+=\s+\w+|\w+\s+=\s+".*?"|\w+\s+=\s+<.*?>)\s*/g;  

$value = [ parse_type_value_specifier( $start_tail ) ];  

You want this instead:

$value = [ parse_type_value_specifier( $value ) ]; 

(Note that the parameter to the function should be $value and not $start_tail.) You probably didn't notice this.

In the loop for @attributes, the 'else' in the if/else condition excutes when the 'value' has a plain value, (no "" or <...> items in 'value').

Update: Changed parameter in

parse_type_value_specifier(...)
to $value. It was (incorrectly) stated as $attrib.

Chris Charley
+1  A: 
drlouie - louierd