c - 有没有更好的方法来确定多个字符范围



我目前正在用C编写代码,它从整个ASCII可用字符中选择符号和数字。作为程序员的初学者,我通常这样做

if ((i > 25 && i < 50) || (i > 100 && i < 200)) { contents } 

对于变量i在 25~50 之间,100~200(不包括)以适应条件。

如果我想设置多个范围,如 32~64( ! to @ ) 和 91~96( [ to ` ) 和 123~126( { to ~ ),那么会有更好的(意味着更短或更简单的代码)还是我应该坚持使用这种方法,继续添加上面的代码中的每个范围?

对于您的具体情况,函数<ctype.h>集合就可以了

if (isprint(i) && !isalpha(i))

额外的好处:它甚至可以在非ASCII系统上工作。

您可以编写一个函数来检查该值是否属于任何给定范围:

struct Range {
        int min;
        int max;
};
bool in_ranges(int character, struct Range *ranges, size_t num_ranges) {
        for(size_t i = 0; i < num_ranges; ++i) {
                if(ranges[i].min < character && character < ranges[i].max)
                        return true;
        }
        return false;
}
int main() {
        struct Range rngs[] = {{25,50}, {100,200}};
        bool at_sign_si_in_range = in_ranges('@', rngs, 2);
        return 0;
}

它使编辑范围变得更加简单并提高可读性。此外,如果您继续像示例中那样在条件子句中编写所有范围,请考虑检查类似

范围
lower_bound < value && value < upper_bound

它看起来像数学符号(x < a < y),似乎也更容易阅读。

如果使用单字节字符,则可以使用标志数组获得更好的性能,设置单个位或整数字节以指示其中一个范围内的字符值。

如果您正在为支持 SSE 4.2 指令的英特尔处理器编写代码,您可能需要考虑使用 PCMPISTRI 或类似指令,它可以将多达 16 个单字节字符与单个指令中的多达 8 个不同范围进行比较。

我的回答是"视情况而定"。 :)

如果isalpha()ctype.h的朋友做你想做的事,那么绝对使用它们。

但如果不是...

如果您只有两个范围,例如您的示例片段,我认为它看起来不会太混乱。如果有更多,可以将范围测试放在(内联)函数中,以减少一次可见的布尔值数量:

if (in_range(val, a1, b1) || in_range(val, a2, b2) || ... )

(或者如果您觉得需要保存屏幕空间,请将其命名为B(n,a,b)

如果范围可能在运行时发生变化,或者范围很多,请将限制放在一个struct中,然后循环访问这些范围数组。如果确实有很多,请对列表进行排序并对其进行一些聪明的操作,例如在下限(或其他)上进行二叉搜索。但是对于少数人,我不会打扰。

如果允许值的总范围很小(例如值为 0..255 的无符号字符),但单独的"范围"的数量很大("所有具有素数值的范围"),则创建值的表(位图),并针对该值进行测试。以您喜欢的任何方式生成表。(isalpha()可能是这样实现的)

unsigned char is_prime[256] = {0, 0, 1, 1, 0, 1, 0, 1, 
    ...};
if (is_prime[val]) { ...

你可以编写一个函数,比如:

bool withinscope(int num, int begin, int end){
    if(num > begin && num < end)
        return true;
    return false;
}

然后您可以使用此功能并保持代码干净简单。

您可以在宏或内联函数中隐藏l<x && x<h的重复,但我发现这很少值得 - 它不像 Python l<x<h语法那样可读,一旦您开始拥有适用于所有包容性边界可能性的宏,很快就会失控。要么你最终得到一个长得离谱的命名约定(between_inc_incbetween_inc_exc、......这有点失败首先排除检查),或者你让读者想知道你的范围检查(" between(i, 50, 100) ...这是一个[,)范围吗?[,]?(检查代码)不,这是一个(,)"),如果你正在逐个寻找错误,这很糟糕。

OTOH,众所周知,我滥用"单字母宏",我准确地定义了需要它们的位置和方式,并且紧随其后未定义。虽然它们可能看起来很丑,但关键是它们非常本地化,并且完全可以完成需要做的事情,因此不会浪费时间查找它们,没有神秘的参数,它们可以分解出大量重复计算。

在您的情况下,如果列表很长,我可能会这样做

#define B(l, h) ((l)<i) && (i<(h)) ||
if(B(25,50) B(100,200) B(220, 240) 0)
... 
#undef B

(永远不要在标题中执行此操作!

相反,使用字符文字而不是 ASCII 数字可以很好地提高可读性:例如,如果您想要 a-z 范围,请执行 'a'<=i && i<='z'

您似乎想要排除按字母顺序排列和不可打印的字符:您可以使用

if((' '<=i && i<'A') || (i>'Z' && i<'a') || ('z'<i && i<=126))
class RangeCollection
{
    std::vector<int> ranges;
public:
    void AddRange(int lowerBound, int upperBound)
    {
        vector.push_back(lowerBound);
        vector.push_back(upperBound);
    }
    bool IsInRange(int num)
    {
        for(int i=0; i<ranges.size()-1; i+=2)
        {
            if(num>ranges[i] && num<ranges[i+1])return true;
        }
     return false;
    }
};

您可以调用AddRange以添加任意数量的范围,然后可以检查数字是否在范围内。

RangeCollection rc;
rc.AddRange(20,25);
rc.IsInRange(22);//returns true

相关内容

  • 没有找到相关文章

最新更新