Как да заснема знак минус в научна нотация с регулярен израз?

Опитвах се да отговоря на въпрос (който по-късно беше изтрит), който според мен задаваше относно извличането на текстови представяния на научна нотация. (Използвайки внедряването на регулярни изрази на R, което изисква двойни екрани за мета-символи и може да се използва както в режим PCRE, така и в Perl, разликата между които наистина не разбирам.) Реших по-голямата част от задачата, но все още изглежда не успява да улови водещия знак минус в рамките на група за улавяне. Единственият начин, по който изглежда да го накарам да успее, е като използвам водещата отворена скоба:

> txt <- c("this is some random text (2.22222222e-200)", "other random (3.33333e4)", "yet a third(-1.33333e-40)", 'and a fourth w/o the "e" (2.22222222-200)')
> sub("^(.+\\()([-+]{0,1}[0-9][.][0-9]{1,16}[eE]*[-+]*[0-9]{0,3})(.+$)", "\\2" ,txt)
[1] "2.22222222e-200" "3.33333e4"       "-1.33333e-40"    "2.22222222-200" 

> sub("^(.+\\()([-+]?[0-9][.][0-9]{1,16}[eE]*[-+]*[0-9]{0,3})(.+$)", "\\2" ,txt)
[1] "2.22222222e-200" "3.33333e4"       "-1.33333e-40"    "2.22222222-200" 
 #but that seems to be "cheating" ... my failures follow:

> sub("^(.+)([-+]?[0-9][.][0-9]{1,16}[eE]*[-+]*[0-9]{0,3})(.+$)", "\\2" ,txt)
[1] "2.22222222e-200" "3.33333e4"       "1.33333e-40"     "2.22222222-200" 
> sub("^(.+)(-?[0-9][.][0-9]{1,16}[eE]*[-+]*[0-9]{0,3})(.+$)", "\\2" ,txt)
[1] "2.22222222e-200" "3.33333e4"       "1.33333e-40"     "2.22222222-200" 
> sub("^(.+)(-*[0-9][.][0-9]{1,16}[eE]*[-+]*[0-9]{0,3})(.+$)", "\\2" ,txt)
[1] "2.22222222e-200" "3.33333e4"       "1.33333e-40"     "2.22222222-200" 

Търсих SO до степента на моето търпение с термини като „научна нотация регулярен израз минус“


person IRTFM    schedule 03.05.2015    source източник
comment
Можете ли да актуализирате въпроса си, за да покажете ясно началния вход и желания изход?   -  person Tim Biegeleisen    schedule 03.05.2015
comment
Въпросът създава вектора на входния знак като вход с помощта на R код и аз публикувах два правилни отговора, които разчитаха на метода, който нарекох измама. Не знам как би могло да бъде по-възпроизводимо   -  person IRTFM    schedule 03.05.2015
comment
@TimBiegeleisen В последните три "1.33333e-40" е различно   -  person akrun    schedule 03.05.2015
comment
Благодаря...разбрах го ^ ^   -  person Tim Biegeleisen    schedule 03.05.2015
comment
С str_extract_all изглежда работи unlist(str_extract_all(txt, '-?[0-9.]+e?[-+]?[0-9]*'))   -  person akrun    schedule 03.05.2015
comment
Публикувайте като отговор и ще приема. Той, разбира се, ще приеме numbers като 161.1.60.40, но това изглежда приемливо.   -  person IRTFM    schedule 03.05.2015
comment
човек може би може да сравни извлечените цифри с тяхното abs() алтер-его и да изолира негативите въз основа на това сравнение.   -  person baptiste    schedule 03.05.2015
comment
първи резултат при гугъл regex заснема реално число - regular-expressions.info/floatingpoint.html   -  person eddi    schedule 15.08.2015
comment
@eddi Това може да е полезна отправна точка, но със сигурност не е пълен R отговор.   -  person IRTFM    schedule 15.08.2015


Отговори (3)


Можете да опитате

 library(stringr)
 unlist(str_extract_all(txt, '-?[0-9.]+e?[-+]?[0-9]*'))
 #[1] "2.22222222e-200" "3.33333e4"       "-1.33333e-40"    "2.22222222-200" 

Използване на метод, базиран на улавяне след водещи скоби

 str_extract(txt, '(?<=\\()[^)]*')
 #[1] "2.22222222e-200" "3.33333e4"       "-1.33333e-40"    "2.22222222-200" 
person akrun    schedule 03.05.2015
comment
Мисля, че stringr сега използва кода в друг пакет, но не виждам споменаване на това в пакета NEWS. - person IRTFM; 03.05.2015
comment
@BondedDust Изглежда така, получавам съобщението, че perl is deprecated. Please use regexp instead. - person akrun; 03.05.2015
comment
@BondedDust Според github.com/hadley/stringr е изграден върху stringi, който използва ICU библиотеката за осигуряване на бързи, правилни реализации на общи манипулации на низове - person akrun; 03.05.2015
comment
Това е пакетът, но не виждам да се споменава за това в моята инсталирана версия. Може би това е само във версията на Github? (Опитах да компилирам на Mac и възникна грешка configure: error: C++ preprocessor "/lib/cpp" fails sanity check .... така че все още съм на CRAN версията.) - person IRTFM; 03.05.2015
comment
@BondedDust Не съм сигурен в това - person akrun; 03.05.2015

Разсъждавайки, че "алчният" капацитет на първата група за улавяне "(.+)" да поглъща знака минус е незадължителен във втората група за улавяне, прекратих първата група за улавяне с клас на отрицателен знак и сега успех. Това все още изглежда тромаво и се надявам да има нещо по-елегантно. При търсене видях код на Python, който изглежда предполага, че има дефиниции на регулярен израз на "&real_number">

> sub("^(.+[^-+])([-+]?[0-9][.][0-9]{1,16}[eE]*[-+]*[0-9]{0,3})(.+$)", "\\2" ,txt,perl=TRUE)
[1] "2.22222222e-200" "3.33333e4"       "-1.33333e-40"    "2.22222222-200" 

След като разгледах кода в str_extract_all, който използва substr за извличане на съвпадения, сега смятам, че трябваше да избера парадигмата gregexpr-regmatches за моите усилия, а не стратегията за избиране на средата от група за улавяне на три:

> hits <- gregexpr('[-+]?[0-9][.][0-9]{1,16}[eE]*[-+]*[0-9]{0,3}', txt)
> ?regmatches
> regmatches(txt, hits)
[[1]]
[1] "2.22222222e-200"

[[2]]
[1] "3.33333e4"

[[3]]
[1] "-1.33333e-40"

[[4]]
[1] "2.22222222-200"
person IRTFM    schedule 03.05.2015

Това изглежда работи и няма да съответства на IP адрес:

sub("^.*?([-+]?\\d+(?:\\.\\d*)*(?:[Ee]?[-+]?\\d+)?).*?$", "\\1", txt)
[1] "2.22222222e-200" "3.33333e4"       "-1.33333e-40"    "2.22222222-200"

Странно, това не е съвсем регулярният израз, с който започнах. Когато един опит не проработи, реших да се върна и да тествам в Perl:

my @txt = (
  "this is some random text (2.22222222e-200)",
  "other random (3.33333e4)",
  "yet a third(-1.33333e-40)" ,
  'and a fourth w/o the "e" (2.22222222-200)');

map { s/^.*?[^-+]([-+]?\d+(?:\.\d*)*(?:[Ee]?[-+]?\d+)?).*?$/$1/ } @txt;

print join("\n", @txt),"\n";

И това изглеждаше добре:

2.22222222e-200
3.33333e4
-1.33333e-40
2.22222222-200

Така че същият регулярен израз трябва да работи в R, нали?

sub("^.*?[^-+]([-+]?\\d+(?:\\.\\d*)*(?:[Ee]?[-+]?\\d+)?).*?$", "\\1", txt)
[1] "0" "4" "0" "0"

Очевидно не. Дори потвърдих, че низът в двойни кавички е правилен, като го опитах в Javascript с new RegExp("...") и там също работи добре. Не съм сигурен какво е различното в R, но премахването на класа на знаците за отричане свърши работа.

person Mark Reed    schedule 03.05.2015
comment
R използва версията на TRE библиотеката на Ville Laurikari (laurikari.net/tre) за не-Perl регулярния израз . - person IRTFM; 03.05.2015