解析C ++ 14整数文字


27

根据http://en.cppreference.com/w/cpp/language/integer_literal,整数文字包含一个十进制/十六进制/八进制/二进制文字和一个可选的整数后缀,这显然是完全不必要的,浪费了宝贵的字节,并且在此挑战中未使用。

十进制文字是a non-zero decimal digit (1, 2, 3, 4, 5, 6, 7, 8, 9), followed by zero or more decimal digits (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

八进制文字是the digit zero (0) followed by zero or more octal digits (0, 1, 2, 3, 4, 5, 6, 7)

十六进制文字是the character sequence 0x or the character sequence 0X followed by one or more hexadecimal digits (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, A, b, B, c, C, d, D, e, E, f, F)(请注意的大小写不敏感abcdefx)。

二进制文字是the character sequence 0b or the character sequence 0B followed by one or more binary digits (0, 1)

另外,可以可选地用's作为数字分隔符。它们没有意义,可以忽略。

输入项

一个字符串,表示C ++ 14整数文字或其字符代码的数组。

输出量

以10为底的输入字符串表示的数字,并带有可选的尾随换行符。正确的输出永远不会超过2 * 10 ^ 9

获奖标准

GCC贡献者需要超过500行代码来执行此操作,因此我们的代码必须尽可能短!

测试用例:

0                       ->    0
1                       ->    1
12345                   ->    12345
12345'67890             ->    1234567890
0xFF                    ->    255
0XfF                    ->    255
0xAbCdEf                ->    11259375
0xa'bCd'eF              ->    11259375
0b1111'0000             ->    240
0b0                     ->    0
0B1'0                   ->    2
0b1                     ->    1
00                      ->    0
01                      ->    1
012345                  ->    5349
0'123'4'5               ->    5349


4
@LuisfelipeDejesusMunoz不; 您如何期望将其解析?
我的代名词是monicareinstate,

1
我认为仅用C ++ 14编写一个函数就是作弊吧?由于编译器已经自动执行该操作(即使它内部是500余行代码...)
Darrel Hoffman

5
@DarrelHoffman但是,您不能仅使用“ C ++ 14中的函数”来执行此操作,因为那样就不会接受字符串输入。也许带有一些调用C ++编译器的脚本。
aschepler

2
该字符串0可能是一个不错的测试用例(它在我的最新修订版中发现了一个错误)。
丹尼尔·谢普勒

Answers:



22

x86(32位)机器代码, 59 57个字节

此函数以esi指向以null结尾的字符串作为指针,并返回中的值edx。(下面列出的是以AT&T语法输入的GAS。)

        .globl parse_cxx14_int
        .text
parse_cxx14_int:
        push $10
        pop %ecx                # store 10 as base
        xor %eax,%eax           # initialize high bits of digit reader
        cdq                     # also initialize result accumulator edx to 0
        lodsb                   # fetch first character
        cmp $'0', %al
        jne .Lparseloop2
        lodsb
        and $~32, %al           # uppercase letters (and as side effect,
                                # digits are translated to N+16)
        jz .Lend                # "0" string
        cmp $'B', %al           # after '0' have either digit, apostrophe,
                                # 'b'/'B' or 'x'/'X'
        je .Lbin
        jg .Lhex
        dec %ecx
        dec %ecx                # update base to 8
        jmp .Lprocessdigit      # process octal digit that we just read (or
                                # skip ' if that is what we just read)   
.Lbin:
        sub $14, %ecx           # with below will update base to 2
.Lhex:
        add $6, %ecx            # update base to 16
.Lparseloop:
        lodsb                   # fetch next character
.Lparseloop2:
        and $~32, %al           # uppercase letters (and as side effect,
                                # digits are translated to N+16)
        jz .Lend
.Lprocessdigit:
        cmp $7, %al             # skip ' (ASCII 39 which would have been
                                # translated to 7 above)
        je .Lparseloop
        test $64, %al           # distinguish letters and numbers
        jz .Lnum
        sub $39, %al            # with below will subtract 55 so e.g. 'A'==65
                                # will become 10
.Lnum:
        sub $16, %al            # translate digits to numerical value
        imul %ecx, %edx
#        movzbl %al, %eax
        add %eax, %edx          # accum = accum * base + newdigit
        jmp .Lparseloop
.Lend:
        ret

以及带有字节数的反汇编-这次采用Intel格式,以防万一您更喜欢那种。

Disassembly of section .text:

00000000 <parse_cxx14_int>:
   0:   6a 0a                   push   0xa
   2:   59                      pop    ecx
   3:   31 c0                   xor    eax,eax
   5:   99                      cdq    
   6:   ac                      lods   al,BYTE PTR ds:[esi]
   7:   3c 30                   cmp    al,0x30
   9:   75 16                   jne    21 <parse_cxx14_int+0x21>
   b:   ac                      lods   al,BYTE PTR ds:[esi]
   c:   24 df                   and    al,0xdf
   e:   74 28                   je     38 <parse_cxx14_int+0x38>
  10:   3c 42                   cmp    al,0x42
  12:   74 06                   je     1a <parse_cxx14_int+0x1a>
  14:   7f 07                   jg     1d <parse_cxx14_int+0x1d>
  16:   49                      dec    ecx
  17:   49                      dec    ecx
  18:   eb 0b                   jmp    25 <parse_cxx14_int+0x25>
  1a:   83 e9 0e                sub    ecx,0xe
  1d:   83 c1 06                add    ecx,0x6
  20:   ac                      lods   al,BYTE PTR ds:[esi]
  21:   24 df                   and    al,0xdf
  23:   74 13                   je     38 <parse_cxx14_int+0x38>
  25:   3c 07                   cmp    al,0x7
  27:   74 f7                   je     20 <parse_cxx14_int+0x20>
  29:   a8 40                   test   al,0x40
  2b:   74 02                   je     2f <parse_cxx14_int+0x2f>
  2d:   2c 27                   sub    al,0x27
  2f:   2c 10                   sub    al,0x10
  31:   0f af d1                imul   edx,ecx
  34:   01 c2                   add    edx,eax
  36:   eb e8                   jmp    20 <parse_cxx14_int+0x20>
  38:   c3                      ret    

如果您想尝试一下,这是我与其链接的C ++测试驱动程序代码(包括GCC asm语法中的调用约定规范):

#include <cstdio>
#include <string>
#include <iostream>

inline int parse_cxx14_int_wrap(const char *s) {
    int result;
    const char* end;
    __asm__("call parse_cxx14_int" :
            "=d"(result), "=S"(end) :
            "1"(s) :
            "eax", "ecx", "cc");
    return result;
}

int main(int argc, char* argv[]) {
    std::string s;
    while (std::getline(std::cin, s))
        std::printf("%-16s -> %d\n", s.c_str(), parse_cxx14_int_wrap(s.c_str()));
    return 0;
}

-1个字节,由Peter Cordes发表

-1字节从更新到使用两个减量将10更改为8


1
只有您缺少针对溢出的测试...编译器报告的数字过多。
亚历克西斯·威尔克

2
你可以换你的寄存器使用rdx和RBX ? Then you can use 1-byte cdq`为零rdxeax
Peter Cordes

1
这应该是列出程序集的字节数,或者被标记为x86机器代码的59字节。
Potato44

2
@PeterCordes谢谢,不知道那件事。(此外,再次查看时,我注意到将基数从10更改为8可能是2个字节-从两个减量指令开始-而不是3个字节。)
Daniel Schepler

3
@AlexisWilke它也不会测试编译器也会做的无效格式(例如,数字超出给定基数的范围)。但是根据问题陈述,可以保证输入有效,并且不会溢出32位有符号整数。
丹尼尔·谢普勒


7

C ++(gcc), 141 138 134 120字节

此函数需要一个字符数组(指定为一对指向起点和终点的指针-使用一对迭代器惯用法)并返回数字。请注意,该函数会更改输入数组。

(这确实依赖于gcc / libstdc ++的行为,该行为#include<cstdlib>还将功能置于全局范围内。对于严格符合标准的代码,请以替换#include<stdlib.h>一个字符的成本来替换。)

简要说明:该代码首先用于std::remove过滤'字符(ASCII 39)。然后,strtol以0为基数的情况将已经处理了十进制,八进制和十六进制的情况,因此要检查的唯一其他情况是前导0b0B,如果这样,请将基数设置为strtol2并在前2个字符之后开始解析。

#import<algorithm>
#import<cstdlib>
int f(char*s,char*e){e=s[*std::remove(s,e,39)=1]&31^2?s:s+2;return strtol(e,0,e-s);}

在线尝试。


由于吊顶猫的建议以及随后的一些打高尔夫球,节省了3个字节。

由于grastropner的建议,节省了4个字节。

卢卡斯-2字节

-12字节乘以l4m2



合并,谢谢。
Daniel Schepler


如果无效输入是未定义的行为,则无需检查0
l4m2

所以124
l4m5



4

R79 71 69字节

`+`=strtoi;s=gsub("'","",scan(,""));na.omit(c(+s,sub("..",0,s)+2))[1]

在线尝试!

strtoi除了以2为基础的转换并忽略之外,它会执行所有操作',因此有很多字节可以解决这些问题。

多亏了亚伦·海曼Aaron Hayman)的-6个字节,并激发了-4个更多的字节(还在增加!)

验证所有测试用例(旧版本)


可以保存一个字节替换sub("0b|B"sub("b|B",因为前导“ 0”不会影响该值。重命名可以获取另一个strtoi
Aaron Hayman,

1
74个字节:在线尝试!
亚伦·海曼

1
@AaronHayman哇,我从没见过na.omit。超级方便,我打了很多球:-)
朱塞佩

1
如果我们假设第一个失败strtoi是二进制文件,则可以使用substring而不是sub保存另一个字节:在线尝试!
亚伦·海曼

1
我们@AaronHayman可以剥离的第一的2个字符ssub代替用sub('..','',s)这是另一种字节较短!
朱塞佩

4

05AB1E16 14字节

多亏了Grimy,节省了2个字节

''KlÐïK>i8ö}.E

在线尝试! 或作为测试套件

说明

''K                # remove "'" from input
   l               # and convert to lower-case
    Ð              # triplicate
     ï             # convert one copy to integer
      K            # and remove it from the second copy
       >i  }       # if the result is 0
         8ö        # convert from base-8 to base-10
            .E     # eval


这是一个伪造的13(通过所有测试用例,但在上失败0010)。
格林尼治标准

@格里米:谢谢!爽用ï
艾米娜(Emigna)

4

Excel,115字节

=DECIMAL(SUBSTITUTE(REPLACE(A1,2,1,IFERROR(VALUE(MID(A1,2,1)),)),"'",),VLOOKUP(A1,{"0",8;"0B",2;"0X",16;"1",10},2))

从A1输入,输出到放置此公式的位置。数组公式,所以使用Ctrl+ Shift+Enter输入。

我添加了几个可以在图像中看到的测试用例-一些早期的尝试正确地处理了所有给定的测试用例,但第16行和/或第17行出错。

在此处输入图片说明


省略最后两个结束括号并利用“编译器”(按Return键或Tab键)会为您错误纠正这一事实是否违反规则?
卢卡斯

我个人认为是的。我认为没有网站共识。Excel加上括号感觉就像是另一种语言的IDE中的代码完成功能一样,对于字节计数应忽略该功能。(但是,我认为“?”在BASIC中应该算作1个字节,即使它会被静默扩展为“ PRINT”,所以在这里我可能并不完全一致)。
索菲亚·莱希纳

3

x86-64机器码,44字节

(相同的机器代码也适用于32位模式。)

@Daniel Schepler的答案是一个起点,但这至少具有一个新的算法思想(不仅仅是更好地打高尔夫)'B'1000010)和'X'1011000)的ASCII码在被掩盖后为16和2。0b0010010

因此,在排除十进制(非零前导数字)和八进制(char after '0'小于'B')之后,我们可以将base =c & 0b0010010并跳入数字循环。

可以在x86-64 System V中作为调用,使用来unsigned __int128 parse_cxx14_int(int dummy, const char*rsi);unsigned __int128结果的上半部分提取EDX返回值tmp>>64

        .globl parse_cxx14_int
## Input: pointer to 0-terminated string in RSI
## output: integer in EDX
## clobbers: RAX, RCX (base), RSI (points to terminator on return)
parse_cxx14_int:
        xor %eax,%eax           # initialize high bits of digit reader
        cdq                     # also initialize result accumulator edx to 0
        lea 10(%rax), %ecx      # base 10 default
        lodsb                   # fetch first character
        cmp $'0', %al
        jne .Lentry2
    # leading zero.  Legal 2nd characters are b/B (base 2), x/X (base 16)
    # Or NUL terminator = 0 in base 10
    # or any digit or ' separator (octal).  These have ASCII codes below the alphabetic ranges
    lodsb

    mov    $8, %cl              # after '0' have either digit, apostrophe, or terminator,
    cmp    $'B', %al            # or 'b'/'B' or 'x'/'X'  (set a new base)
    jb   .Lentry2               # enter the parse loop with base=8 and an already-loaded character
         # else hex or binary. The bit patterns for those letters are very convenient
    and    $0b0010010, %al      # b/B -> 2,   x/X -> 16
    xchg   %eax, %ecx
    jmp  .Lentry

.Lprocessdigit:
    sub  $'0' & (~32), %al
    jb   .Lentry                 # chars below '0' are treated as a separator, including '
    cmp  $10, %al
    jb  .Lnum
    add  $('0'&~32) - 'A' + 10, %al   # digit value = c-'A' + 10.  we have al = c - '0'&~32.
                                        # c = al + '0'&~32.  val = m+'0'&~32 - 'A' + 10
.Lnum:
        imul %ecx, %edx
        add %eax, %edx          # accum = accum * base + newdigit
.Lentry:
        lodsb                   # fetch next character
.Lentry2:
        and $~32, %al           # uppercase letters (and as side effect,
                                # digits are translated to N+16)
        jnz .Lprocessdigit      # space also counts as a terminator
.Lend:
        ret

与Daniel的版本相比,已更改的块的缩进程度(比其他指令少)。主循环的底部也有其条件分支。事实证明,这是中立的变更,因为任何一条路径都无法落入其顶端,dec ecx / loop .Lentry进入循环想法在以不同的方式处理八进制之后并不是赢家。但是它在循环内部的指令较少,而惯用形式的循环在结构中是do {} while结构,因此我保留了它。

Daniel的C ++测试工具使用此代码在64位模式下无法更改,该代码使用与他的32位答案相同的调用约定。

g++ -Og parse-cxx14.cpp parse-cxx14.s &&
./a.out < tests | diff -u -w - tests.good

反汇编,包括作为实际答案的机器代码字节

0000000000000000 <parse_cxx14_int>:
   0:   31 c0                   xor    %eax,%eax
   2:   99                      cltd   
   3:   8d 48 0a                lea    0xa(%rax),%ecx
   6:   ac                      lods   %ds:(%rsi),%al
   7:   3c 30                   cmp    $0x30,%al
   9:   75 1c                   jne    27 <parse_cxx14_int+0x27>
   b:   ac                      lods   %ds:(%rsi),%al
   c:   b1 08                   mov    $0x8,%cl
   e:   3c 42                   cmp    $0x42,%al
  10:   72 15                   jb     27 <parse_cxx14_int+0x27>
  12:   24 12                   and    $0x12,%al
  14:   91                      xchg   %eax,%ecx
  15:   eb 0f                   jmp    26 <parse_cxx14_int+0x26>
  17:   2c 10                   sub    $0x10,%al
  19:   72 0b                   jb     26 <parse_cxx14_int+0x26>
  1b:   3c 0a                   cmp    $0xa,%al
  1d:   72 02                   jb     21 <parse_cxx14_int+0x21>
  1f:   04 d9                   add    $0xd9,%al
  21:   0f af d1                imul   %ecx,%edx
  24:   01 c2                   add    %eax,%edx
  26:   ac                      lods   %ds:(%rsi),%al
  27:   24 df                   and    $0xdf,%al
  29:   75 ec                   jne    17 <parse_cxx14_int+0x17>
  2b:   c3                      retq   

Daniel版本的其他更改包括sub $16, %al通过使用more sub代替test检测分隔符的一部分),以及数字与字母字符。

与丹尼尔不同,下面的每个字符都'0'被视为分隔符,而不仅仅是'\''。(除' 'and $~32, %al/ jnz在我们的两个循环中,/ 都将空格视为终止符,这对于在行首使用整数进行测试可能很方便。)

%al在循环内部进行修改的每个操作都有一个使用结果设置的分支消耗标志,并且每个分支都(或经过)到不同的位置。


您是否还需要初始化,eax使AIUI在目标位置较小的64位模式操作码中将较高的位重置为0?
Daniel Schepler

@Daniel:写一个32位寄存器零扩展到64位。写入8或16位寄存器可防止其他模式的行为:合并为现有值。AMD64并未解决8位和16位寄存器的错误相关性,也没有更改setcc r/m8setcc r/m32,因此我们仍然需要一个愚蠢的2指令xor-zero / set flags / setcc %alsequence创建32/64位0或1变量,并且需要在标志设置之前将寄存器清零。(或mov $0, %eax改为使用,或movzx在关键路径上使用)。
Peter Cordes

1

视网膜,96字节

T`'L`_l
\B
:
^
a;
a;0:x:
g;
a;0:b:
2;
a;0:
8;
[a-g]
1$&
T`l`d
+`;(\d+):(\d+)
;$.($`*$1*_$2*
.+;

在线尝试!链接包括测试套件。说明:

T`'L`_l

删除's并将所有内容都转换为小写。

\B
:

分隔数字,因为任何十六进制数字都需要转换为十进制。

^
a;
a;0:x:
g;
a;0:b:
2;
a;0:
8;

确定数字的基数。

[a-g]
1$&
T`l`d

将字符转换a-g为数字10-16

+`;(\d+):(\d+)
;$.($`*$1*_$2*

在数字列表上执行基数转换。$.($`*$1*_*$2*是短期的$.($`*$1*_*$2*_),它乘$`$1在一起,并增加了$2。($`是字符串在;基数之前的部分。)

.+;

删除基础。


我很欣赏的文字编程方法,你走上解释代码:-)
grooveplex

1

J,48个字节

cut@'0x 16b +0b 2b +0 8b0 '''do@rplc~'+',tolower

在线尝试!

字符串替换后的Eval。

0XfF -> +16bff -> 255
0xa'bCd'eF -> +16babcdef -> 11259375
0B1'0 -> +2b10 -> 2
0 -> 8b0 -> 0
01 -> 8b01 -> 1
0'123'4'5 -> 8b012345 -> 5349

它似乎不适用于包含0b以下内容的
Galen Ivanov,

1
@GalenIvanov很好的发现,已修复
FrownyFrog

1

Perl 6,29个字节

{+lc S/^0)>\d/0o/}o{S:g/\'//}

在线尝试!

Perl 6需要一个明确的 0o八进制前缀,并且不支持像这样的大写前缀0X

说明

                   {S:g/\'//}  # remove apostrophes
{                }o  # combine with function
     S/^0)>\d/0o/    # 0o prefix for octal
  lc  # lowercase
 +    # convert to number

1

八度29 21 20字节

@(x)str2num(x(x>39))

在线尝试!

-8个字节,感谢@TomCarpenter


对于22个字节:@(x)str2num(x(x~="'"))
汤姆·卡彭特

变为21个字节:@(x)str2num(x(x~=39))
汤姆·卡彭特

八进制似乎不起作用(至少在TIO上)...例如,应在63时f=("077")返回ans = 77。或者,如OP中的测试用例f=("012345")应返回5349,而是ans = 12345
brhfl

1

Bash,33个字节

x=${1//\'};echo $[${x/#0[Bb]/2#}]

蒂奥

Zsh,29 27字节

-2个字节,感谢@GammaFunction

<<<$[${${1//\'}/#0[Bb]/2#}]

蒂奥


聪明!我本以为setopt octalzeroesZsh是必要的。
GammaFunction

您可以使用Zsh保存2个字节,<<<$[...]而不是echo $[...]
GammaFunction

谢谢,我不知道带有重定向的zsh empty命令可以显示输出,我对zsh不太了解,我知道更好的bash
Nahuel Fouilleul

我知道bash会自动解释以零到八进制
开头的

0

出发75

import "strconv"
func(i string)int64{n,_:=strconv.ParseInt(i,0,0);return n}

这似乎不适用于二进制文字或单引号数字定界符。
尼克·马特奥

真是的 我会尽快修复。完全忘记了定界符
vityavv

0

JavaScript(ES6),112字节

n=>+(n=n.toLowerCase().replace(/'/g,""))?n[1]=="b"?parseInt(n.substr(2),2):parseInt(n,+n[0]?10:n[1]=="x"?16:8):0







0

C ++,G ++,189字节

#include<fstream>
#include<string>
void v(std::string s){{std::ofstream a("a.cpp");a<<"#include<iostream>\nint main(){std::cout<<"<<s<<";}";}system("g++ -std=c++14 a.cpp");system("a.exe");}

无需测试

需要安装 g++ C ++ 14支持

现在,说明:

它写入一个名为的文件a.cpp,使用GCC对其进行编译,并给出一个输出数字的文件



0

Pyth,27个字节

Jscz\'?&qhJ\0}@J1r\0\7iJ8vJ

在线尝试!

与以前的(现已删除的)Pyth答案不同,此答案通过了问题中的所有测试用例,尽管它长了3个字节。


欢迎光临本站!
小麦巫师

0

C(gcc)/ Bash / C ++,118个字节

f(i){asprintf(&i,"echo \"#import<iostream>\nmain(){std::cout<<%s;}\">i.C;g++ i.C;./a.out",i);fgets(i,i,popen(i,"r"));}

在线尝试!


我打了一些代码。然后,我意识到根本没有任何理由让它起作用,但是它似乎起作用了。158个字节
我的代词是monicareinstate

@someone,很讨厌,但是我喜欢!
Johan du Toit

通过合并popen和148个字节system。我认为G ++有一个标志-x可以从stdin中读取。这可能会超出FOPEN东西短,但我不知道如何与C.标准输入调用
我的代名词是monicareinstate

@someone,现在一切都合并到popen命令中了
Johan du Toit

printf-> echo似乎有效。您将很快使用bash进行编程。
我的代名词是monicareinstate

0

爪哇158字节

这只是等待被淘汰。只是尝试使用正则表达式,直到某些东西可以工作并且默认为十六进制。
-4个字节,感谢@ValueInk

n->{n=n.replace("'","");var s=n.split("[bBxX]");return Long.parseLong(s[s.length-1],n.matches("0[bB].+")?2:n.matches("0\\d+")?8:n.matches("\\d+")?10:16);}

在线尝试

使用ScriptEngine,92 87字节

评估列车通过。从技术上讲,这正将火炬传递给JS,所以这不是我的主要建议。

n->new javax.script.ScriptEngineManager().getEngineByName("js").eval(n.replace("'",""))

蒂奥


使用[bBxX]0[bB].+快速进行正则表达式优化。
价值墨水

@ValueInk谢谢
本杰明·厄克特

这,不是一个Integer这是一个长期,标题明确表示整数,单或双精度的方法用于保存在IEEE754时,由于小数位系统的号码IEEE754可能成为不正确由于en.wikipedia.org/wiki/ IEEE_754#Roundings_to_nearest,它还支持大于2万亿(0x9999999999)的数字
Martin Barker

@MartinBarker允许将其用于Long代替Integer高尔夫目的。另外,如果您是对的,Python将无法竞争,因为它具有有效的任意精度整数。另外,longJava中的a 是用64位而不是32位表示的整数。没有小数位。
本杰明·厄克特

Long的意思是,您使用long而不是整数,并且打高尔夫球的目的是错误的,The correct output never will exceed 2*10^9它很清楚地表明,long不能单独使用,因为我可以给定它0x9999999999,它将产生一个数字高于2 * 10 ^ 9,而C ++则会产生内存溢出问题,因为当您仅为该数字分配32位内存时,您在内存中使用了32位以上
Martin Barker
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.