用分隔符分割字符串不能正确分割

时间:2018-12-26 11:37:05

标签: c string split

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

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

3 个答案:

答案 0 :(得分:0)

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

{编辑添加}

您的程序假定每个单词后面都有一个定界符,因为仅当isDelimiter(delimiter, line[i])为真( inWord 不为0)时才显示该单词,因此,如果文件中恰好包含未显示涡轮直接喷射(最后一个字符为'n')

答案 1 :(得分:0)

主要问题是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]),根据C语言中的定义,char1

  • 如果ferror(filePointer)返回有效指针,则对fgets()进行测试是没有意义的。

  • 匹配i时,您必须将/*加2,否则会误解序列/*/,此外,您不应继续在注释中扫描{{1} }和/或字符串定界符。

  • 如果出于相同的原因匹配//,则必须退出循环。

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

  • 与字符常量相同的注释。

  • 如果文件包含很长的标识符(> 99个字符),包括内部注释或字符串,则可能会有缓冲区溢出。

  • 您忘记关闭源文件

答案 2 :(得分:-1)

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;
}