将a-z扩展为abc ... xyz形式的方法

时间:2011-11-02 18:44:02

标签: c algorithm

嗨:)我正在尝试做的是编写一个简单的程序,以便从最短的条目扩展

例如

  

a-z或0-9或a-b-c或a-z0-9

写得最长

例如

  

abc ... xyz或0123456789或abc或abcdefghijklmnouprstwxyz0123456789

1-st examle shortst entry = 1-example示例结果,应该给出:))

到目前为止,我写了类似这样的内容,它仅适用于从a到z的字母:

expand(char s[])
{
   int i,n,c;
   n=c=0;
   int len = strlen(s);
   for(i = 1;s[i] > '0' && s[i]<= '9' || s[i] >= 'a' && s[i] <= 'z' || s[i]=='-';i++)
   {
      /*c = s[i-1];
      g = s[i];
      n = s[i+1];*/
      if( s[0] == '-')
          printf("%c",s[0]);
      else if(s[i] == '-')
      {
         if(s[i-1]<s[i+1])
         {
             while(s[i-1] <= s[i+1])
             {
                 printf("%c", s[i-1]);
                 s[i-1]++;
             }
         }
         else if(s[i-1] == s[i+1])
             printf("%c",s[i]);
         else if(s[i+1] != '-')
             printf("%c",s[i]);
         else if(s[i-1] != '-')
             printf("%c",s[i]);
    }
    else if(s[i] == s[i+1])
    {

      while(s[i] == s[i+1])
      {
           printf("%c",s[i]);
           s[i]++;
      }
    }
    else if( s[len] == '-')
         printf("%c",s[len]);
 }

}

但现在我被卡住了:(

任何想法我应该检查我的程序是否正常工作?

编辑1: @Andrew Kozak(1)abcd(2)01234

感谢您提前:)

5 个答案:

答案 0 :(得分:3)

以下是满足 same test as my earlier C++ version.

的C版本(约38条有效行

可以在http://ideone.com/sXM7b#info_3915048

上看到完整的测试程序,包括您的测试用例,我的和一些酷刑测试

原理

我很确定我夸大了要求,但

  • 这应该是如何以健壮的方式进行解析的一个很好的例子
    • 以明确的方式使用状态
    • 验证输入(!)
      • 此版本不认为a-c-b不会发生
      • 它也不会像“Hello World”(或(char*) 0
      • 这样的简单输入窒息甚至失败
  • 它显示了如何在不使用无关功能的情况下避免printf("%c", c)每个字符。
  • 我提出了一些意见,以解释为什么会发生什么,但总的来说,你会发现代码更加清晰,无论如何,

    • 远离太多短命名变量
    • 使用不透明的索引器来避免复杂的条件
    • 避免整个字符串长度业务:我们只需要2个字符的最大预测,*it=='-'predicate(*it)只返回 false ,如果它是空字符。 Shortcut evaluation阻止我们访问过去的输入字符
  • 一个警告:我没有对输出缓冲区溢出执行了正确的检查(容量在2048个字符处硬编码)。我将把它留给读者作为众所周知的练习

最后但并非最不重要的一点,我做了这个原因:

  • 这将允许我比较C ++版本和此C版本的原始性能,因为它们执行等效功能。 现在,我完全期望C版本能够超越C ++(让我们猜测:4倍?)但是,让我们再看看GNU编译器为我们带来了多少惊喜。 以后更多 更新结果我离我不远:github (code + results)

Pure C Implementation

不用多说,实现,包括测试用例:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int alpha_range(char c) { return (c>='a') && (c<='z'); }
int digit_range(char c) { return (c>='0') && (c<='9'); }

char* expand(const char* s)
{
    char buf[2048];

    const char* in  = s;
          char* out = buf;

    // parser state
    int (*predicate)(char) = 0; // either: NULL (free state), alpha_range (in alphabetic range), digit_range (in digit range)
    char lower=0,upper=0;       // tracks lower and upper bound of character ranges in the range parsing states

    // init
    *out = 0;

    while (*in)
    {
        if (!predicate)
        {
            // free parsing state
            if (alpha_range(*in) && (in[1] == '-') && alpha_range(in[2]))
            {
                lower = upper = *in++;
                predicate = &alpha_range;
            }
            else if (digit_range(*in) && (in[1] == '-') && digit_range(in[2]))
            {
                lower = upper = *in++;
                predicate = &digit_range;
            } 
            else *out++ = *in;
        } else
        { 
            // in a range
            if (*in < lower) lower = *in;
            if (*in > upper) upper = *in;

            if (in[1] == '-' && predicate(in[2])) 
                in++; // more coming
            else
            {
                // end of range mode, dump expansion
                char c;
                for (c=lower; c<=upper; *out++ = c++);
                predicate = 0;
            }
        }
        in++;
    }

    *out = 0; // null-terminate buf
    return strdup(buf);
}

void dotest(const char* const input)
{
    char* ex = expand(input);
    printf("input : '%s'\noutput: '%s'\n\n", input, ex);

    if (ex)
        free(ex);
}

int main (int argc, char *argv[])
{
    dotest("a-z or 0-9 or a-b-c or a-z0-9"); // from the original post
    dotest("This is some e-z test in 5-7 steps; this works: a-b-c. This works too: b-k-c-e. Likewise 8-4-6"); // from my C++ answer
    dotest("-x-s a-9 9- a-k-9 9-a-c-7-3"); // assorted torture tests

    return 0;
}

测试输出:

input : 'a-z or 0-9 or a-b-c or a-z0-9'
output: 'abcdefghijklmnopqrstuvwxyz or 0123456789 or abc or abcdefghijklmnopqrstuvwxyz0123456789'

input : 'This is some e-z test in 5-7 steps; this works: a-b-c. This works too: b-k-c-e. Likewise 8-4-6'
output: 'This is some efghijklmnopqrstuvwxyz test in 567 steps; this works: abc. This works too: bcdefghijk. Likewise 45678'

input : '-x-s a-9 9- a-k-9 9-a-c-7-3'
output: '-stuvwx a-9 9- abcdefghijk-9 9-abc-34567'

答案 1 :(得分:2)

好的,我测试了你的程序,似乎几乎适用于所有情况。它只用两个字母/数字正确地扩展了a-z和其他扩展。当有更多的字母和数字时,它会失败。修复很简单,只需创建一个新的字符来保留最后打印的字符,如果当前打印的字符与最后一个字符匹配则跳过它。 a-z0-9场景不起作用,因为你忘了s [i]&gt; ='0'而不是s [i]&gt; '0'。代码是:

#include <stdio.h>
#include <string.h>

void expand(char s[])
{
        int i,g,n,c,l;
    n=c=0;
    int len = strlen(s);
    for(i = 1;s[i] >= '0' && s[i]<= '9' || s[i] >= 'a' && s[i] <= 'z' || s[i]=='-';i++)
    {
        c = s[i-1];
        g = s[i];
        n = s[i+1];
        //printf("\nc = %c g = %c n = %c\n", c,g,n);
        if(s[0] == '-')
            printf("%c",s[0]);
        else if(g == '-')
        {
            if(c<n)
            {
                if (c != l){
                    while(c <= n)
                    {
                        printf("%c", c);
                        c++;
                    }
                    l = c - 1;
                //printf("\nl is %c\n", l);
            }
            else
            {
                c++;
                while(c <= n)
                {
                    printf("%c", c);
                    c++;
                }
                l = c - 1;
                //printf("\nl is %c\n", l);
            }
        }
        else if(c == n)
            printf("%c",g);
        else if(n != '-')
            printf("%c",g);
        else if(c != '-')
            printf("%c",g);
    }
    else if(g == n)
    {

        while(g == n)
        {
            printf("%c",s[i]);
            g++;
        }
    }
    else if( s[len] == '-')
        printf("%c",s[len]);
    }
    printf("\n");
}

int main (int argc, char *argv[])
{
    expand(argv[1]);
}

这不是K&amp; R的问题吗?我想我在那里看到了它。无论如何,我希望我帮助过。

答案 2 :(得分:1)

基于现有函数分别处理“a-z”和“0-9”序列的事实,我们应该探讨它们相遇时会发生什么。跟踪你的代码(尝试在每一步打印每个变量的值 - 是的它会混乱,所以使用换行符),我相信你会在迭代时发现逻辑短路,例如,从“当前令牌是'y '和下一个标记是'z'“到”当前标记是'z'而下一个标记是'0'“。探索if()条件,你会发现它并没有涵盖所有可能性,即你已经覆盖了自己,如果你在&lt; - &gt; z范围内,在0&gt; 9范围内,或者完全等于' - ',但是你没有考虑在下一个角色的下一个角色处于一个(az或0-9)的末尾。

答案 3 :(得分:1)

为了好玩,我决定向自己证明C ++真的适合这种事情。

先测试,

首先,让我更严格地定义要求:我认为它需要处理这些情况:

<子>

int main()
{
    const std::string in("This is some e-z test in 5-7 steps; this works: a-b-c. This works too: b-k-c-e. Likewise 8-4-6");
    std::cout << "input : " << in         << std::endl;
    std::cout << "output: " << expand(in) << std::endl;
}
  

input : 这是 5 -7 步骤中的一些 e-z 测试;这有效: a-b-c 。这也有效: b-k-c-e 。同样 8-4-6   
output: 这是 567 步骤中的 efghijklmnopqrstuvwxyz 测试;这有效: abc 。这也有效: bcdefghijk 。同样 45678   

C ++ 0x实现

以下是C ++ 0x代码 1 (23包括空格,注释) 的实现(实际上是一些变体) >

static std::string expand(const std::string& in)
{
    static const regex re(R"([a-z](?:-[a-z])+|[0-9](?:-[0-9])+)");

    std::string out;

    auto tail = in.begin();
    for (auto match : make_iterator_range(sregex_iterator(in.begin(), in.end(), re), sregex_iterator()))
    {
        out.append(tail, match[0].first);

        // char range bounds: the cost of accepting unordered ranges...
        char a=127, b=0;
        for (auto x=match[0].first; x<match[0].second; x+=2)
            { a = std::min(*x,a); b = std::max(*x,b); }

        for (char c=a; c<=b; out.push_back(c++));
        tail = match.suffix().first;
    }
    out.append(tail, in.end());

    return out;
}

当然我作弊有点因为我正在使用Boost的regex iterators。我将针对性能与C版本进行比较。我宁愿期望C ++版本在50%的范围内竞争。但是,让我们看看GNU编译器为我们存储了什么样的惊喜:)

这是一个演示样本输入的完整程序。 _它还包含一些基准时间和一些折衷的变化

  • 功能灵活性
  • 易读性/表现


#include <set> // only needed for the 'slow variant'
#include <boost/regex.hpp>
#include <boost/range.hpp>

using namespace boost;
using namespace boost::range;

static std::string expand(const std::string& in)
{
//  static const regex re(R"([a-z]-[a-z]|[0-9]-[0-9])"); // "a-c-d" --> "abc-d", "a-c-e-g" --> "abc-efg"
    static const regex re(R"([a-z](?:-[a-z])+|[0-9](?:-[0-9])+)");

    std::string out;
    out.reserve(in.size() + 12); // heuristic

    auto tail = in.begin();
    for (auto match : make_iterator_range(sregex_iterator(in.begin(), in.end(), re), sregex_iterator()))
    {
        out.append(tail, match[0].first);

        // char range bounds: the cost of accepting unordered ranges...
#if !SIMPLE_BUT_SLOWER
        // debug 15.149s / release 8.258s (at 1024k iterations)
        char a=127, b=0;
        for (auto x=match[0].first; x<match[0].second; x+=2)
        { a = std::min(*x,a); b = std::max(*x,b); }

        for (char c=a; c<=b; out.push_back(c++));
#else   // simpler but slower
        // debug 24.962s / release 10.270s (at 1024k iterations)
        std::set<char> bounds(match[0].first, match[0].second);
        bounds.erase('-');
        for (char c=*bounds.begin(); c<=*bounds.rbegin(); out.push_back(c++));
#endif
        tail = match.suffix().first;
    }
    out.append(tail, in.end());

    return out;
}

int main()
{
    const std::string in("This is some e-z test in 5-7 steps; this works: a-b-c. This works too: b-k-c-e. Likewise 8-4-6");
    std::cout << "input : " << in         << std::endl;
    std::cout << "output: " << expand(in) << std::endl;
}

<子> 1 g++-4.6 -std=c++0x一起编译

答案 4 :(得分:0)

这是一个 Java 实现。它扩展了类似于 0-9、a-z 和 A-Z 的字符范围。也许有一天有人会需要它,而 Google 会将他们带到此页面。

package your.package;

public class CharacterRange {

    /**
     * Expands character ranges similar to 0-9, a-z and A-Z.
     * 
     * @param string a string to be expanded
     * @return a string
     */
    public static String expand(String string) {

        StringBuilder buffer = new StringBuilder();

        int i = 1;
        while (i <= string.length()) {
            final char a = string.charAt(i - 1); // previous char
            if ((i < string.length() - 1) && (string.charAt(i) == '-')) {
                final char b = string.charAt(i + 1); // next char
                char[] expanded = expand(a, b);
                if (expanded.length != 0) {
                    i += 2; // skip
                    buffer.append(expanded);
                } else {
                    buffer.append(a);
                }
            } else {
                buffer.append(a);
            }
            i++;
        }

        return buffer.toString();
    }

    private static char[] expand(char a, char b) {
        char[] expanded = expand(a, b, '0', '9'); // digits (0-9)
        if (expanded.length == 0) {
            expanded = expand(a, b, 'a', 'z'); // lower case letters (a-z)
        }
        if (expanded.length == 0) {
            expanded = expand(a, b, 'A', 'Z'); // upper case letters (A-Z)
        }
        return expanded;
    }

    private static char[] expand(char a, char b, char min, char max) {

        if ((a > b) || !(a >= min && a <= max && b >= min && b <= max)) {
            return new char[0];
        }

        char[] buffer = new char[(b - a) + 1];
        for (int i = 0; i < buffer.length; i++) {
            buffer[i] = (char) (a + i);
        }
        return buffer;
    }

    public static void main(String[] args) {

        String[] ranges = { //
                "0-9", "a-z", "A-Z", "0-9a-f", "a-z2-7", "0-9a-v", //
                "0-9a-hj-kmnp-tv-z", "0-9a-z", "1-9A-HJ-NP-Za-km-z", //
                "A-Za-z0-9", "A-Za-z0-9+/", "A-Za-z0-9-_" };

        for (int i = 0; i < ranges.length; i++) {

            String input = ranges[i];
            String output = CharacterRange.expand(ranges[i]);

            System.out.println("input:  " + input);
            System.out.println("output: " + output);
            System.out.println();
        }
    }
}

输出:

input:  0-9
output: 0123456789

input:  a-z
output: abcdefghijklmnopqrstuvwxyz

input:  A-Z
output: ABCDEFGHIJKLMNOPQRSTUVWXYZ

input:  0-9a-f
output: 0123456789abcdef

input:  a-z2-7
output: abcdefghijklmnopqrstuvwxyz234567

input:  0-9a-v
output: 0123456789abcdefghijklmnopqrstuv

input:  0-9a-hj-kmnp-tv-z
output: 0123456789abcdefghjkmnpqrstvwxyz

input:  0-9a-z
output: 0123456789abcdefghijklmnopqrstuvwxyz

input:  1-9A-HJ-NP-Za-km-z
output: 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz

input:  A-Za-z0-9
output: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789

input:  A-Za-z0-9+/
output: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/

input:  A-Za-z0-9-_
output: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_