【问题标题】:Splitting string by delimiter does not split correctly按分隔符拆分字符串未正确拆分
【发布时间】:2018-12-26 11:37:05
【问题描述】:

我有一个函数必须通过某些分隔符将从文件读取的行溢出到单词中(分隔符检查由另一个函数进行)但我的代码通过分隔符列表中未包含的分隔符拆分字符串 [一个数组分隔符]:

void getIdentifiers() {
    FILE *filePointer;
    char fileName[FILENAME_MAX], line[256], identifier[100];

    char delimiter[] = {
        '[', ']', '(', ')', ';', '!', '=', '>', '<', '|', '*', '/', ':',
        '&', '%', ' ', '\n', '\t', '"', '{', '}', ',', '-', '+', '#' };

    int cnt = 0, inWord = 0, isString = 0, isSingleLineComment = 0, isMultiComment = 0, isChar = 0;

    printf("\n Enter filename:\n");
    flush(stdin);

    if (!fgets(fileName, FILENAME_MAX, stdin)) {
        printf("\nError reading filename");
        return;
    }

    if (fileName[strlen(fileName) - 2] != 'c' || fileName[strlen(fileName) - 3] != '.') {
        printf("\nInvalid source file name\n");
        return;
    }

    fileName[strlen(fileName) - 1] = '\0';

    filePointer = fopen(fileName, "r");

    if (filePointer == NULL) {
        printf("\nError opening file");
        return;
    }

    while (fgets(line, sizeof(line) / sizeof(line[0]), filePointer) != NULL) {
        if (ferror(filePointer)) {
            printf("\nError reading the file");
            return;
        }

        int i = 0, j = 0;

        while (line[i]) {
            //multi line comment check
            if (line[i] == '/' && line[i + 1] == '*')
                isMultiComment = 1;

            //single line comment
            if (line[i] == '/' && line[i + 1] == '/')
                isSingleLineComment = 1;

            //ending multi line comment
            if (line[i] == '*' && line[i + 1] == '/' && isMultiComment == 1) 
                isMultiComment = 0;

            //checking for string
            if (line[i] == '"' && isString == 0)
                isString = 1;
            else if (line[i] == '"' && isString == 1)
                isString = 0;

            //check if assignment char is in quote
            if (line[i] == '\'' && isChar == 0) 
                isChar = 1;
            else if (line[i] == '\'' && isChar == 1)
                isChar = 0;

            //splitting textline into words
            if (inWord==0) {
                if (!isDelimiter(delimiter, line[i])) {
                    inWord = 1;
                    identifier[j] = line[i];
                    j++;
                } else {
                    i++;
                    continue;
                }
            } else {
                //ending word
                if (isDelimiter(delimiter, line[i])) {
                    inWord = 0;
                    identifier[j] = 0;
                    j = 0;

                    // identifier checking
                    if (!isString && !isMultiComment && !isSingleLineComment 
                        && !isChar) {
                        cnt++;
                        printf("\n%s", identifier);
                    }
                } else {
                    identifier[j] = line[i];
                    j++;
                }
            }
            i++;
        }
        isSingleLineComment = 0;
    }
    printf("\n Number of identifiers is  %d", cnt);
}

int isDelimiter(char *delim, char c) {
    int i = 0;
    while (delim[i]) {
        if (delim[i] == c)
            return 1;
        i++;
    }
    return 0;
}

我尝试读取的文件包含:

Turbo direct injection

预期的输出是:

Turbo
direct
injection

但我得到了:

Turbo
di
ect
inject
o

【问题讨论】:

  • 它有帮助,但现在没有显示最后一个字
  • 除了将delimiter 视为非空终止字符串之外,还有其他一些事情......嗯,奇怪(或错误):首先,调用fflush on C 规范中明确提到了仅输入流(如stdin)作为未定义的行为。即使某些 C 库实现了它,它也不是可移植的,应该避免使用。此外,由于sizeof(char) 被指定为始终为1,表达式sizeof(line)/sizeof(line[0]) 等于sizeof(line)。如果fileName 短于两个字符会怎样?还是比FILENAME_MAX更长?
  • 在使用它之前声明 isDelimiter,或将其定义移到其使用之上

标签: c string split


【解决方案1】:

您的输入文件末尾是否有换行符?

{编辑添加}

您的程序假设每个单词后面都有一个分隔符,因为该单词仅在 isDelimiter(delimiter, line[i]) 为真时打印(inWord 不为 0),所以如果文件包含完全 Turbo直接注入(最后一个字符是'n')注入不显示

【讨论】:

  • 不,文件末尾没有换行
  • 这就是为什么没有显示注入,你的程序假设每个单词后面都有一个分隔符
  • 这不是答案,而是评论。
  • @Someprogrammerdude 我想这足以让他理解,我在回答中添加了更多内容,谢谢你的反对票,哈哈
【解决方案2】:

主要问题是 delimiter 字符数组不是正确的 C 字符串:您必须在初始化程序的末尾添加一个显式的空字节:

    char delimiter[] = { '[', ']', '(', ')', ';', '!', '=', '>', '<',
        '|', '*', '/', ':', '&', '%', ' ', '\n', '\t', '"', '{', '}',
        ',', '-', '+', '#', '\0' };

或者,您可以使用字符串常量:

    char delimiter[] = "[]();!=><|*/:&% \n\t\"{},-+#";

您的代码中还有很多问题:

  • flush(stdin) 具有未定义的行为。如果您的意思是丢弃先前调用 scanf() 的待处理输入,请使用显式循环。

  • 您应该从fileName 中删除尾随换行符,但不要假设它存在。以这一衬里为例:

    fileName(strcspn(fileName, "\n")] = '\0';
    
  • 您应该在从末尾访问字符之前验证字符串长度。按照编码,如果用户在换行符之前输入单个字符,则您有未定义的行为。首先去掉换行符然后使用这个:

    size_t len = strlen(fileName);
    if (len < 3 || fileName[len - 2] != '.' || fileName[len - 1] != 'c') {
        printf("\nInvalid source file name\n");
        return;
    }
    
  • 不需要sizeof(line) / sizeof(line[0])char 在 C 中定义为 1

  • 如果 fgets() 返回一个有效指针,则对 ferror(filePointer) 的测试毫无意义。

  • 1234563 /p>
  • 如果匹配//,出于同样的原因,您必须退出循环。

  • 如果您在字符串中,您应该将\ 识别为转义字符并接受\" 作为字符串的一部分。此外,您不应扫描字符串中的 cmets。

  • 字符常量的注释相同。

  • 如果文件包含很长的标识符(> 99 个字符),包括在 cmets 或字符串中,您可能会出现缓冲区溢出。

  • 您忘记关闭源文件

【讨论】:

  • 先生,我已经看到了你很多精彩的答案。你能看看t1p.de/xu1t吗??
【解决方案3】:
char **mystrtok(const char *str, const char *del, int alowempty)
{
  char **result = NULL;
  const char *end = str;
  size_t size = 0;
  int extrachar;

  while(*end)
  {
    if((extrachar = !!strchr(del, *end)) || !*(end + 1))
    {
        /* add temp variable and malloc / realloc checks */
        /* free allocated memory on error */
        if(!(!alowempty && !(end - str)))
        {
            extrachar = !extrachar * !*(end + 1);
            result = realloc(result, (++size + 1) * sizeof(*result));
            result[size] = NULL;
            result[size -1] = malloc(end - str + 1 + extrachar);
            strncpy(result[size -1], str, end - str + extrachar);
            result[size -1][end - str + extrachar] = 0;
        }
        str = end + 1;
    }
    end++;
  }
  return result;
}

【讨论】:

  • 这很有趣,因为你有一个类似于 Yusmen Rashidov 的问题,如果输入字符串没有被分隔符终止,那么最后一个字符就会丢失(不是那个时候的单词,除非最后一个单词的长度是 1 ),例如mystrtok("azety qsd", " ", 0) 返回{"azerty", "qs", 0} (mystrtok("azety qsd ", " ", 0) 返回{"azerty", "qsd", 0})
  • 补充说明,如果str长度为1,为什么返回NULL?如果mystrtok("a", " ", 0) 返回{ "a", 0} 否似乎更实用?
  • 是的。排序------
  • 我看你很喜欢'!' :-) 在extrachar = !extrachar * !*(end + 1); 中使用 '*' 作为 and 似乎很昂贵,不是吗?
  • @bruno 没有。有两个原因: 1. 生成的代码不会multiplyand 的结果。 2. 即使它将在所有现代平台(包括 8 位 AVR uC)上进行整数乘法运算,速度也与and 相同。这次你的评论是 100% 错误的
猜你喜欢
  • 1970-01-01
  • 2012-10-29
  • 1970-01-01
  • 2020-10-03
  • 1970-01-01
  • 1970-01-01
  • 2011-06-09
  • 1970-01-01
  • 2017-12-08
相关资源
最近更新 更多