C/C++使用大小写开关匹配所有字母[关闭]

c3frrgcw  于 2023-02-26  发布在  C/C++
关注(0)|答案(5)|浏览(176)

3小时前关门了。
截至3小时前,社区正在审查是否重新讨论此问题。
Improve this question
我有一个很长的if () ... else if () ... else if() ...代码,类似于:

int token;
if((token >= 'a' && token <= 'z') || (token >= 'A' && token <= 'Z'))
    // ...
else if (token == '\n')
    // ...
else if (token == '^')
    // ...
else if (token == '&')
    // ...

它有很多'=='token >= 'a' && token <= 'Z'的作用域,所以我想用switch重写这个if else,但是用case匹配所有字母表很麻烦,我知道它可以写成下面的代码:

int token;
switch (token) {
    case 'a':
    case 'b':
    case 'c':
    case 'd':
    case 'e':
    case 'f':
    case 'g':
    case 'h':
    case 'i':
    case 'j':
    case 'k':
    case 'l':
    case 'm':
    case 'n':
    case 'o':
    case 'p':
    case 'q':
    case 'r':
    case 's':
    case 't':
    case 'u':
    case 'v':
    case 'w':
    case 'x':
    case 'y':
    case 'z':
        // ...
        break;

}

但我认为这并不简洁,所以我想问是否有更简洁的方法来使用一个case来匹配a-zA-Z

p8h8hvxi

p8h8hvxi1#

使用表格简化switch()。@user3386109

// Table look-up of character to switch index.
  static const unsigned char type[UCHAR_MAX + 1u] = { //
      ['A'] = 1, ['B'] = 1, ['C'] = 1, ['D'] = 1,['E'] = 1, //
      ['F'] = 1, ['G'] = 1, ['H'] = 1, ['I'] = 1,['J'] = 1, //
      ['K'] = 1, ['L'] = 1, ['M'] = 1, ['N'] = 1,['O'] = 1, //
      ['P'] = 1, ['Q'] = 1, ['R'] = 1, ['S'] = 1,['T'] = 1, //
      ['U'] = 1, ['V'] = 1, ['W'] = 1, ['X'] = 1,['Y'] = 1, //
      ['Z'] = 1, //
      ['a'] = 1, ['b'] = 1, ['c'] = 1, ['d'] = 1,['e'] = 1, //
      ['f'] = 1, ['g'] = 1, ['h'] = 1, ['i'] = 1,['j'] = 1, //
      ['k'] = 1, ['l'] = 1, ['m'] = 1, ['n'] = 1,['o'] = 1, //
      ['p'] = 1, ['q'] = 1, ['r'] = 1, ['s'] = 1,['t'] = 1, //
      ['u'] = 1, ['v'] = 1, ['w'] = 1, ['x'] = 1,['y'] = 1, //
      ['z'] = 1, //
      ['\n'] = 2, //
      ['^'] = 3, //
      ['&'] = 4, //
      // Other elements are 0 since they are not explicitly initialized.
      };

  unsigned char token;
  switch (type[token]) {
    case 1: ...  break; // letters
    case 2: ...  break; // \n
    case 3: ...  break; // ^
    case 4: ...  break; // &
    default: // None of the above.
  }

这在某种程度上复制了is...()例程,但没有 * locale * variance * 1和2)可以根据您的解析需要进行定制。
最好使用命名常量/enum,而不是1,2,3,4 ...

    • 速度**

我怀疑OP正在使用这段代码进行标记化,并且属于3%的时候,微优化是值得的。

  • 1高级:许多is...()函数都有一些 * locale * 依赖性。例如:"在" C "区域设置中,isalpha仅对isupperislower为true的字符返回true。" C17dr § www.example.com 27.4.1.2 2

这主要影响非ASCII(0 - 127)范围的字符。当为特定协议进行标记化时,请记住is...()函数有 * locale * 变量。

wz3gfoph

wz3gfoph2#

使用switch中的case表示专用字符,使用default:标签表示范围:

switch (token) {
case '\n':
  // ...
  break;
case '&':
  // ...
  break;
case '^':
  // ...
  break;
default:
  if (std::isalpha(token)) {
    // ...
  }
  break;
}

或者有点不寻常

if (std::isalpha(token)) {
  // ...
} else switch (token) {
case '\n':
  // ...
  break;
case '&':
  // ...
  break;
case '^':
  // ...
  break;
}
mbyulnm0

mbyulnm03#

我想问一下,是否有一种更简洁的方式来使用大小写匹配a-z和A-Z。
是的,有。

int token;
if((token >= 'a' && token <= 'z') || (token >= 'A' && token <= 'Z'))
    // ...

可以简化为:

#include <ctype.h>

int token;
if(isalpha((unsigned char)token))
    // ...

isalpha()检查字母字符,如果字符c属于测试类,则返回非零值,否则返回零值。
对于其余的符号,可以使用-preferred-switch()语句,或者使用if/else梯形图,ctype.h中声明的函数可以帮助简化事情。
正如@Mike所评论的,GCC提供了一个有用的扩展:Case Ranges (Using the GNU Compiler Collection (GCC)).
也可以在这里看到@Toby的答案,看看discrepancy between the two solutions

b5buobof

b5buobof4#

我喜欢chux使用查找表的方法,在下面的源代码中,这是swchux
它产生:

movzbl %dil,%edi
movzbl 0(%rdi),%eax

然后,4-5:

cmp $x,%al
jxx ...

这对于数量有限的case语句来说确实很快。
但是,如果case语句的数量较大,则cmp/jxx条目会占用大量时间。
我遇到过一个switch/case块有一百个左右的条目的情况,所以它不能扩展。
通过使用计算的后藤(使用&&label),我们可以将其简化为(在swfix1中):

movzbl %dil,%edi
movzbl 0(%rdi),%eax
jmp *tbl(,%rax,8)

对于我的用例,使用计算的后藤而不是switch将总体性能提高了30%。
使用一些cpp宏,我们可以使语法类似于switch/case块。
在上面的例子中,我们使用的是unsigned char查找,如果我们使用直接标签表,我们可以减少一条指令(在swfix2中):

movzbl %dil,%edi
jmpq   *0x0(,%rdi,8)

这样就省去了一条asm指令,代价是查找表使用8字节/条目(而上面的查找表使用1字节)。
下面是上述示例的.c源代码。
注意,这里我只是使用DOIT宏作为case中实际代码的占位符,在真实的代码中,每个case都有自己的/不同的代码。

#include <limits.h>

int state;

#define DOIT(val_) \
    state = 256 + val_

static const unsigned char type[UCHAR_MAX + 1u] = { //
    ['A'] = 1,['B'] = 1,['C'] = 1,['D'] = 1,['E'] = 1,  //
    ['F'] = 1,['G'] = 1,['H'] = 1,['I'] = 1,['J'] = 1,  //
    ['K'] = 1,['L'] = 1,['M'] = 1,['N'] = 1,['O'] = 1,  //
    ['P'] = 1,['Q'] = 1,['R'] = 1,['S'] = 1,['T'] = 1,  //
    ['U'] = 1,['V'] = 1,['W'] = 1,['X'] = 1,['Y'] = 1,  //
    ['Z'] = 1,                      //
    ['a'] = 1,['b'] = 1,['c'] = 1,['d'] = 1,['e'] = 1,  //
    ['f'] = 1,['g'] = 1,['h'] = 1,['i'] = 1,['j'] = 1,  //
    ['k'] = 1,['l'] = 1,['m'] = 1,['n'] = 1,['o'] = 1,  //
    ['p'] = 1,['q'] = 1,['r'] = 1,['s'] = 1,['t'] = 1,  //
    ['u'] = 1,['v'] = 1,['w'] = 1,['x'] = 1,['y'] = 1,  //
    ['z'] = 1,                      //
    ['\n'] = 2,                     //
    ['^'] = 3,                      //
    ['&'] = 4,                      //
    // Other elements are 0 since they are not explicitly initialized.
};

void
swchux(unsigned char token)
{

    switch (type[token]) {
    case 1:
        DOIT(1);
        break;                          // letters
    case 2:
        DOIT(2);
        break;                          // \n
    case 3:
        DOIT(3);
        break;                          // ^
    case 4:
        DOIT(4);
        break;                          // &
    default:                            // None of the above.
        DOIT(0);
        break;
    }
}

#define CASE(idx_) \
    CASE_##idx_
#define V(case_) \
    &&CASE(case_)

#undef SWITCH
#define SWITCH(idx_) \
    goto *swvec[idx_]

void
swfix1(unsigned char token)
{

    static void *swvec[5] = {
        V(0),
        V(1),
        V(2),
        V(3),
        V(4),
    };

    do {
        SWITCH(type[token]);

        CASE(1):
            DOIT(1);
            break;                          // letters
        CASE(2):
            DOIT(2);
            break;                          // \n
        CASE(3):
            DOIT(3);
            break;                          // ^
        CASE(4):
            DOIT(4);
            break;                          // &
        CASE(0):
            DOIT(0);
            break;
    } while (0);
}

#undef SWITCH
#define SWITCH(idx_) \
    goto *swvec[idx_]

void
swfix2(unsigned char token)
{

    static const void *swvec[UCHAR_MAX + 1u] = {    //
        ['A'] = V(1),['B'] = V(1),['C'] = V(1),['D'] = V(1),['E'] = V(1),   //
        ['F'] = V(1),['G'] = V(1),['H'] = V(1),['I'] = V(1),['J'] = V(1),   //
        ['K'] = V(1),['L'] = V(1),['M'] = V(1),['N'] = V(1),['O'] = V(1),   //
        ['P'] = V(1),['Q'] = V(1),['R'] = V(1),['S'] = V(1),['T'] = V(1),   //
        ['U'] = V(1),['V'] = V(1),['W'] = V(1),['X'] = V(1),['Y'] = V(1),   //
        ['Z'] = V(1),                       //
        ['a'] = V(1),['b'] = V(1),['c'] = V(1),['d'] = V(1),['e'] = V(1),   //
        ['f'] = V(1),['g'] = V(1),['h'] = V(1),['i'] = V(1),['j'] = V(1),   //
        ['k'] = V(1),['l'] = V(1),['m'] = V(1),['n'] = V(1),['o'] = V(1),   //
        ['p'] = V(1),['q'] = V(1),['r'] = V(1),['s'] = V(1),['t'] = V(1),   //
        ['u'] = V(1),['v'] = V(1),['w'] = V(1),['x'] = V(1),['y'] = V(1),   //
        ['z'] = V(1),                       //
        ['\n'] = V(2),                      //
        ['^'] = V(3),                       //
        ['&'] = V(4),                       //
        // Other elements are 0 since they are not explicitly initialized.
    };

    do {
        SWITCH(token);

        CASE(1):
            DOIT(1);
            break;                          // letters
        CASE(2):
            DOIT(2);
            break;                          // \n
        CASE(3):
            DOIT(3);
            break;                          // ^
        CASE(4):
            DOIT(4);
            break;                          // &
        CASE(0):
            DOIT(0);
            break;
    } while (0);
}

以下是使用-S构建的源代码:

.file   "all.c"
    .text
    .p2align 4,,15
    .globl  swchux
    .type   swchux, @function
swchux:
.LFB0:
    .cfi_startproc
    movzbl  %dil, %edi
    movzbl  type(%rdi), %eax
    cmpb    $2, %al
    je  .L2
    jbe .L10
    cmpb    $3, %al
    je  .L6
    cmpb    $4, %al
    jne .L5
    movl    $260, state(%rip)
    ret
    .p2align 4,,10
    .p2align 3
.L10:
    cmpb    $1, %al
    jne .L5
    movl    $257, state(%rip)
    ret
    .p2align 4,,10
    .p2align 3
.L2:
    movl    $258, state(%rip)
    ret
    .p2align 4,,10
    .p2align 3
.L5:
    movl    $256, state(%rip)
    ret
    .p2align 4,,10
    .p2align 3
.L6:
    movl    $259, state(%rip)
    ret
    .cfi_endproc
.LFE0:
    .size   swchux, .-swchux
    .p2align 4,,15
    .globl  swfix1
    .type   swfix1, @function
swfix1:
.LFB1:
    .cfi_startproc
    movzbl  %dil, %edi
    movzbl  type(%rdi), %eax
    jmp *swvec.1969(,%rax,8)
    .p2align 4,,10
    .p2align 3
.L17:
    movl    $256, state(%rip)
    ret
    .p2align 4,,10
    .p2align 3
.L16:
    movl    $260, state(%rip)
    ret
    .p2align 4,,10
    .p2align 3
.L15:
    movl    $259, state(%rip)
    ret
    .p2align 4,,10
    .p2align 3
.L14:
    movl    $258, state(%rip)
    ret
    .p2align 4,,10
    .p2align 3
.L12:
    movl    $257, state(%rip)
    ret
    .cfi_endproc
.LFE1:
    .size   swfix1, .-swfix1
    .p2align 4,,15
    .globl  swfix2
    .type   swfix2, @function
swfix2:
.LFB2:
    .cfi_startproc
    movzbl  %dil, %edi
    jmp *swvec.1979(,%rdi,8)
    .p2align 4,,10
    .p2align 3
.L23:
    movl    $260, state(%rip)
    ret
    .p2align 4,,10
    .p2align 3
.L22:
    movl    $259, state(%rip)
    ret
    .p2align 4,,10
    .p2align 3
.L21:
    movl    $258, state(%rip)
    ret
    .p2align 4,,10
    .p2align 3
.L19:
    movl    $257, state(%rip)
    ret
    .cfi_endproc
.LFE2:
    .size   swfix2, .-swfix2
    .section    .rodata
    .align 32
    .type   swvec.1979, @object
    .size   swvec.1979, 2048
swvec.1979:
    .zero   80
    .quad   .L21
    .zero   216
    .quad   .L23
    .zero   208
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .zero   24
    .quad   .L22
    .zero   16
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .quad   .L19
    .zero   1064
    .align 32
    .type   swvec.1969, @object
    .size   swvec.1969, 40
swvec.1969:
    .quad   .L17
    .quad   .L12
    .quad   .L14
    .quad   .L15
    .quad   .L16
    .align 32
    .type   type, @object
    .size   type, 256
type:
    .zero   10
    .byte   2
    .zero   27
    .byte   4
    .zero   26
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .zero   3
    .byte   3
    .zero   2
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .byte   1
    .zero   133
    .comm   state,4,4
    .ident  "GCC: (GNU) 8.3.1 20190223 (Red Hat 8.3.1-2)"
    .section    .note.GNU-stack,"",@progbits
jk9hmnmh

jk9hmnmh5#

我想问一下,是否有更简洁的方法使用大小写来匹配a-z和A-Z
GCC具有扩展名:

void foo(char x)
{
    switch(x)
    {
        case 'a' ... 'z':
            printf("Lower case letter\n");
            break;
        case 'A' ... 'Z':
            printf("Upper case letter\n");
            break;
        case '0' ... '9':
            printf("Digit\n");
            break;
        default:
            printf("Something else\n");
            break;
    }
}

当然,它不能使用非GCC系列编译器进行编译。

相关问题