Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Perl: Общие вопросы > Как определить, является ли строка числом?


Автор: Andrey1 15.6.2004, 11:28
Как определить, является ли значение строковой переменной числом?

(как известно, в перле сравнения численные и символьные (> и gt) отличаются wink.gif )



Спасибо.


Андрей

Автор: HalkaR 15.6.2004, 12:18
Код
/^-?(?:d+(?:\.\d*)?|\.\d+)$/

Автор: Andrey1 15.6.2004, 12:33
Если первое d -> \d, то будет правильно.

Код
/^-?(?:\d+(?:\.\d*)?|\.\d+)$/


Только, правда, не понимаю, зачем ?: hehe.gif

Автор: HalkaR 15.6.2004, 15:55
(?: ... ) это я уж не помню что, но это форма такая smile.gif

Автор: Nobody 15.6.2004, 17:15
?: используется, если нужно объединение в группу, но при этом не нужно запоминание в массив с найденными совпадениями.

Автор: korob2001 15.6.2004, 22:33
Цитата

?: используется, если нужно объединение в группу, но при этом не нужно запоминание в массив с найденными совпадениями.

Почему в массив?
$1, $2 и т.д , помоему являются, специальными, скалярными переменными.

Автор: Nobody 16.6.2004, 00:21
Опс... Сорри, давно с Перлом не общался) Ну, я думаю, меня все поняли.

Автор: Secandr 16.6.2004, 08:22
а если так:
Код
if ($a eq ($a-0)){}

Автор: Nobody 16.6.2004, 23:58
А что есть число? 0 - это число? Если число, то любая строка является числом.

Автор: Secandr 17.6.2004, 08:37
Nobody
Насколько я понимаю нужно проверить:
$a="Вася Пупкин" или $a=12

Хотя в перле разница между числом и не числом бовольно зыбкая: "Вася пупкин"==0 есть true

Автор: Nobody 17.6.2004, 08:43
Secandr
Вот-вот. Обычно проще принудительно сделать переменную числом (например, прибавив нуль), чем париться с тем, что она из себя представляет.

Автор: Secandr 17.6.2004, 08:55
Nobody
В пхп и перле всегда принятые числовые значения из формы обрабатываю $a+=0; smile.gif
Незнаю можно ли сделать проверку быстрее smile.gif

Автор: Nobody 17.6.2004, 09:12
Secandr
не, в PHP я делаю немного грамотней:
Код
settype($var, "integer");


Ой, щас нас бить за оффтоп начнут... rolleyes.gif

Автор: MuToGeN 17.6.2004, 10:02
А что, в перле чего-то типа PHPшного is_numeric() или is_int() нет?

Автор: Nobody 17.6.2004, 10:12
А просто не очень понятно, зачем надо. Нужно тебе число - так сделай с переменной что-нибудь, чтобы она точно стала числом, и не парься.

Автор: HalkaR 17.6.2004, 17:43
MuToGeN , нету.

В Перле вообще нет разницы между строкой и числом. Если ты сравниваешь пременные eq, то они сравниваются как строки, если ==, то как числа.

Автор: pompei 6.12.2007, 07:12
Во-первых: в перле числа и строки отличаются.
Во-вторых: если они отличаются, то должа быть задача, где нужно их отличать, например, сериалайзинг. 
В-третих: И должен быть способ как это сделать.

Люди! Давайте всё таки разберёмся, как это сделать!!!!!!!!!!!!

Информация для размышления:

В перле есть возможность отличить строку от числа. Посмотрите вывод программы:
Код

use Data::Dumper;
print Dumper 1;
print Dumper "1";

Вывод такой:
Код

$VAR1 = 1;
$VAR1 = '1';

От сюда сразу напрашивается решение сделать что-то типа этого:
Код

use Data::Dumper;
sub isNumber {
  my $x = shift;
  my $y = Dumper( $x );
  return $y !~ /'/; #'если есть апостроф, то у нас строка
}

print isNumber( 1 ) ? "число\n" : "строка\n";
print isNumber( "1" ) ? "число\n" : "строка\n";

Вывод такой:
Код

число
строка

Но это очень уродливый пирожёк.
Проследить как работает функция Dumper не получается, извилин не хватает. Может у кого получиться?

Люди давайте разберёмся, а!!!

Автор: Ramirez 6.12.2007, 12:46
Собственно, сам data dumper определяет следующим образом:

Код

$val =~ /^(?:0|-?[1-9]\d{0,8})\z/ # safe decimal number


или вот еще вариант:

Код

# regular expression matching a plain number
$RE_NUMBER = qr/
    (?<!\w)                         # start of a number, not the continuation of an identifier like 'cap12'
    [+-]?                           # optional sign
    (?:(?:\d+(?:\.\d*)?)|(?:\.\d+)) # mantissa
    (?:e[+-]?\d+)?                  # optional exponent
    /ix;


интересно было-бы увидеть надежный способ без регекспов.

Автор: amg 6.12.2007, 13:04
Цитата(Ramirez @  6.12.2007,  12:46 Найти цитируемый пост)
Собственно, сам data dumper определяет следующим образом:
Не, это не то. И 1 и '1' подходят под это рег. выражение, да иначе и быть. Наверное, чтобы определить, где число, а где строка, надо смотреть на их представление непосредственно в памяти. Только я никак не могу придумать, зачем бы это могло понадобиться.

Автор: pompei 6.12.2007, 13:43
Мне это нужно для передачи данных в JavaScript.
Например, перловскую конструкцию:
Код

my $x = {
  asd=> 1,
  dsa=> '1',
  u=> [123, '123'],
};

перевести в строку: '{ "asd": 1, "dsa" : "1", "u" : [123, "123"] }'
которую передать в JavaScript, где прогнать её через фукнцию eval, и получить уже яваскриптовую конструкцию. Есть вариант всё представлять в виде строк, но это не красиво, и в общем то меня не устраивает.

Добавлено @ 13:45
Цитата(Ramirez @ 6.12.2007,  12:46)
Код

$val =~ /^(?:0|-?[1-9]\d{0,8})\z/ # safe decimal number


Это не то. для 1 и "1" - это условие даёт один результат, а мне надо чтобы отличатлся.

Dumper-же как-то отличает строку от числа. Вопрос: как он это делает? Найти бы то место, где он это делает, и сделать так же. У меня это не получается. И дебаггер внутрь там одной функции лесть не хочет - проскакивает как "step over", когда я командую "step into".

У меня пока вот так это реалицуется:
Код

#-------------------------------------------------------------------------------
sub _strToJSStr {
    my $s = shift;
    return "$s" if _isNumber( $s );
    $s =~ s/"/" + '"' + "/gm; #"
    $s =~ s/\\/\\\\/gm;
    $s =~ s/\n/\\n/gm;
    $s =~ s/\r/\\r/gm;
    return '"' . $s . '"';
}
#-------------------------------------------------------------------------------
sub _isNumber {
    my $x = shift;
    use Data::Dumper;
    my $y = Dumper( $x );
    return $y !~ /'/; #если есть апостроф, то у нас строка'
    # уродливая функция
}
#-------------------------------------------------------------------------------
sub serializeToJS {
    my $data = shift;
    return 'null' unless defined $data;
    my $ref = ref $data;
    if ($ref eq 'HASH') {
        my $ret = "{";
        my $first = 1;
        while (my ($k, $v) = each %$data) {
            if ($first) {
                $first = 0;
            } else {
                $ret .= ", ";
            }
            $ret .= _strToJSStr( $k ) . ' : ' . serializeToJS( $v );
        }
        return $ret . "}";
    }
    if ($ref eq 'ARRAY') {
        my $ret = '[';
        my $first = 1;
        for my $d (@$data) {
            if ($first) {
                $first = 0;
            } else {
                $ret .= ", ";
            }
            $ret .= serializeToJS( $d );
        }
        return $ret . ']';
    }
    if ($ref eq '') {
        return _strToJSStr( $data );
    }
    die "Cannot serialize $ref";
}
#-------------------------------------------------------------------------------

Автор: amg 6.12.2007, 14:11
Кстати, насчет eval: 
Код

print Dumper '1';
print Dumper eval('1');


Автор: amg 6.12.2007, 14:32
И как насчет этого?
Код

$_ = '{ "asd": 1, "dsa" : "1", "u" : [123, "123"] }';
s/"((?:[^"]|(?<=\\)")*)"/is_number($1) ? $1 : "\"$1\""/eg;
sub is_number { shift =~ /^(?:0|-?[1-9]\d{0,8})\z/ }
print;


Автор: pompei 6.12.2007, 14:51
Наоборот. Мне надо сложную структуру данных с перла передать в яваскрипт (иерархию сколькоугодно вложенных друг в друга массивов и хэшей содержащие числа и строки), чтобы на яваскрипте была точно такая же иерархия. Я преобразую эту структуру с помощью функции serializeToJS в строку, которую понимает яваскриптовый eval.

Автор: amg 6.12.2007, 15:57
Цитата(pompei @  6.12.2007,  13:43 Найти цитируемый пост)
Dumper-же как-то отличает строку от числа. Вопрос: как он это делает? Найти бы то место, где он это делает, и сделать так же. У меня это не получается. И дебаггер внутрь там одной функции лесть не хочет - проскакивает как "step over", когда я командую "step into".
Думаю, что Dumper отличает строку от числа с помощью сишных функций, обращаясь непосредственно к памяти. Возможно, потому и не получается "step into". И если попробовать Data::Dupm (это pure perl) вместо Data::Dumper, то не увидим никакой разницы между 1 и "1".

Добавлено через 2 минуты и 30 секунд
Цитата(pompei @  6.12.2007,  14:51 Найти цитируемый пост)
Наоборот. <...>
Торможу...

Автор: KSURi 6.12.2007, 17:22
Эээ, а не проще делать так?
Код

if(is_numeric($var)) { print 'ok' }
else { print 'not ok' }

sub is_numeric { int shift }


UPD:
Сори, прочитал весь топик, понял, что не то. Буду думать)

Автор: Ramirez 6.12.2007, 17:32
Так раз вам JSON на выходе нужен, почему бы не воспользоваться 
http://search.cpan.org/~makamaka/JSON-1.15/lib/JSON.pm
а еще лучше
http://search.cpan.org/~mlehmann/JSON-XS-2.01/XS.pm

хотя, он тоже не различает, он все числа без кавычек выдает. разве это критично?

UPDATE: 

JSON-XS в отличие от обычного JSON различает числа в кавычках и нет.

расковырял Data::Dumper и выяснилось, что: он может работать через XS или средствами Perl. По дефолту он использует XS-реализацию. (XS как раз сильно типизирован в отличе от перла, поэтому он правильно обрабатывает числа в кавычках). А вот если Data::Dumper попросить не использовать XS, то он перестает отличать числа от чисел в кавычках:

Код

use strict;

$a = [ 1 , '1' ];


use Data::Dumper;

print "Dumping using XS:\n";
print Dumper($a);

$Data::Dumper::Useperl=1;

print "Dumping using Perl:\n";
print Dumper($a);


на основании этого, смею предположить, что если вы будете использовать XS-реализацию JSON, то получите то, что хотите.

Код

use JSON;                                                                                                                           
use JSON::XS;                                                                                                                       
                                                                                                                                    
$a = [ 1 , '1' ];
                                                                                                                                    
print 'JSON: '.objToJson($a)."\n";                                                                                                  
print 'JSON XS: '.JSON::XS->new->utf8->encode ($a)."\n";


или, если вы принципиально хотите сериализировать самостоятельно, без сторонних модулей, то вам необходимо реализовать подобную проверку на Си и с помощью XS импортировать ее в Perl. Собственно, решение можно подсмотреть в xs-файлах того-же Data::Dumper и JSON-XS.

По другому, видимо - никак....

Автор: KSURi 6.12.2007, 18:53
Наковырял чуть подробнее по XS.

На уровне XS перл представляет скаляр как структуру (SV). В нее может быть загужено 4 типа значений:
  • целочисленное значение (IV)
  • число с плавающей запятой двойной точности (NV)
  • строка (PV)
  • другое скалярное значение (SV)
Для проверки типа хранимого значения (целое; с плавающей точкой; указатель на строку) используются соотвественно следующие макросы:
  • SvIOKp(SV*)
  • SvNOKp(SV*)
  • SvPOKp(SV*)

Автор: vadiml 6.12.2007, 22:31
есть еще и SvUOK(), но с ней проблемы в 64битной сборке
а на уровне XS это действительно делается элементарно:
CheckType.xs
Код

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

Module=CheckType    Package=CheckType

PROTOTYPES: ENABLE

int
ChechVar(SV* sv)
    CODE:
        if ( SvOK(sv) )
            RETVAL=0; // не скаляр
        else if ( SvIOK(sv) )
            RETVAL=1;
        else if (SvNOK(sv))
            RETVAL=2;
        else
            RETVAL=3;
   OUTPUT:
       RETVAL

CheckType.pm
Код

Package CheckType;
require DynaLoader;

our @ISA = qw(DynaLoader);
our $VERSION = '0.01';

bootstrap CheckType $VERSION;

1;


но все проверки достаточно условны, т.е. проверятся в каком контексте последний раз использовалась эта переменная.
а на уровне перла есть только контекст текущего использования

Автор: pompei 7.12.2007, 08:35
Я искренне всем благодарен за помощь.

ЗЫ. Меня несколько огорчило то, что в чистом перле нельзя это проверить. :(

Автор: pompei 7.12.2007, 08:56
Цитата(Ramirez @ 6.12.2007,  12:46)

интересно было-бы увидеть надежный способ без регекспов.

Кстати именно для Вашего случая есть решение без герэкспов, довольно надёжное:
Код

use strict;

sub is_number {
    my $x = shift;
    return $x eq (0 + $x);
}

print is_number( "123" ) ? "number" : "string";
print "\n";
print is_number( "asd" ) ? "number" : "string";
print "\n";

вывод:
Код

number
string


Единственное что is_number( "                 123" ) ложно. Но если перед return добавить $x =~ s/^\s+//; то всё нормально. Хотя может быть это как раз таки и не нужно.


 

Автор: tishaishii 7.12.2007, 14:55
Дак чем не устраивают перечисленные варианты: регвыр или приведение к числу?
Самое простое:
Код
$x+0 eq $x


А если число записано в x16 или x8?
Посложнее:
Код
^\d+(?:\.\d*|x\d+)?|\.\d+$


Добавлено через 6 минут и 57 секунд
А ещё в Perl можно записывать числа так: 1______000_0
Код
^0x\d+|\d[\d_]*|\.\d[\d_]*|\d[\d_]*\.|\d[\d_]*\.\d[\d_]*$

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)