2

我问了一个类似的问题,但遗漏了一个重要的细节。我正在对字符数组(cstring)进行一些文本处理。输入数组被复制到输出数组,除了某些字符被改变(例如 a->b)。这是通过使用 switch 语句来完成的。我想要的是,如果在一行中找到两个或多个特定字符,则只有其中一个被复制到新数组中(所以我不希望一行中有两个空格)。

这就是我到目前为止所得到的,它可以在不跳过某些字符的一行中的两个或多个的情况下工作:

char cleanName[ent->d_namlen];
    for(int i = 0; i < ent->d_namlen; i++)
    {
        switch(ent->d_name[i])
        {

            case 'a' :
                cleanName[i] = 'b';//replace a's with b's (just an example)
                break;
            case ' ' ://fall through
            case '-' :
            case '–' :
            case '—' :
                cleanName[i] = '_';//replace spaces and dashes with spaces
                break;
            ....//more case statments
           default: 
                cleanName[i] = ent->d_name[i];
  }
}

例如,如果一行中的两个字符被下划线替换,我该怎么做?我只执行switch语句if(ent->d_name[i] != previous || (ent->d_name[i] != '-' && ent->d_name[i] != '_' && ent->d_name[i] != ' ')

这可能更像是一个算法问题,而不是特定于实现的问题。

示例输入:abbc--de
输出:bbbc_d_e(为简单起见,假设“a”映射到“b”,但实际上不止于此)

4

5 回答 5

2

好吧,对于这类文本处理算法,我会使用状态机。

于 2012-08-31T19:54:05.043 回答
2

在现有转换之后,最简单的方法是std::unique与自定义谓词一起使用:

cleanName.erase(std::unique(std::begin(cleanName), std::end(cleanName),
    [](char c, char d) { return c == '_' && d == '_'; }), std::end(cleanName));

对于char数组:

length = std::unique(cleanName, &cleanName[length],
    [](char c, char d) { return c == '_' && d == '_'; }) - cleanName;
cleanName[length] = '\0';
于 2012-08-31T19:54:21.857 回答
0

需要注意的一件事是,根据索引的工作方式,您需要意识到 cleanName 的长度不一定与输入数组的长度相同。因此,您需要小心索引 i。

于 2012-08-31T19:55:26.420 回答
0

我的建议是将最后找到的字符保留在临时变量中。这样,如果出现相同的字符,您可以忽略它。有两种方法可以做到这一点:
在 switch 之后添加一个 while 语句,它消耗每个字符等于找到的最后一个字符。如果 cstring 有很多重复字符,这很有用:

 char cleanName[ent->d_namlen];
 char parent;
    for(int i = 0; i < ent->d_namlen; i++)
    {
        switch(ent->d_name[i])
        {
            case 'a' :
                cleanName[i] = 'b';//replace a's with b's (just an example)
                parent = ent->d_name[i];
                break;
            case ' ' ://fall through
            case '-' :
            case '–' :
            case '—' :
                cleanName[i] = '_';//replace spaces and dashes with spaces
                parent = ent->d_name[i];
                break;
            ....//more case statments
           default: 
                cleanName[i] = ent->d_name[i];
        }
        while((parent == ent->d_name[i++]) && ent->d_name[i++] != NULL)
            i++;
    }

这里唯一的问题是,如果您有一系列交错的空格和破折号,它可能无法识别它并保留各种“_”。一种解决方案是保留父母的集合而不是单个字符。

另一种方法是在每次迭代中将当前字符与下一个字符进行比较。您保留“父”变量,并在每个切换案例中将当前字符与父字符进行比较,如果它们相同,则不要将其添加到 cleanName cstring。

于 2012-08-31T20:09:07.553 回答
0

unique_copy这是一个关于算法和transform_iterator来自 Boost的想法:

#include <boost/iterator/transform_iterator.hpp>
#include <iterator>
#include <iostream>
#include <string>
#include <algorithm>

char transform(char c)
{
    switch(c) {
        case 'a' :
            return 'b';
        case ' ' :
        case '-' :
        case '–' :
        case '—' :
            return '_';
        default:
            return c;
    }
}

int main()
{
    std::string in = "abbc--d-e";
    std::string out;

    std::unique_copy(
        boost::make_transform_iterator(in.begin(), transform),
        boost::make_transform_iterator(in.end(), transform),
        std::back_inserter(out),
        [](char c1, char c2){ return c1 == '_' && c2 == '_'; });

    std::cout << out; // bbbc_d_e
}
于 2012-08-31T21:29:29.420 回答