file - 使用 C 程序打印 file 中的所有短语

我需要打印 file 中的所有短语(短语可以以 '.'、'?' 或 '!' 结尾)

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

char* read_file(char *name) {
    FILE *file;
    char *text;
    long num_bytes;

    file = fopen(name, "r");

    if(!file) {
        printf("File could not be opened!");
        exit(EXIT_FAILURE);
    }

    fseek(file, 0, SEEK_END);
    num_bytes = ftell(file);
    fseek(file, 0, SEEK_SET);

    text = (char*) malloc(num_bytes * sizeof(char));
    fread(text, 1, num_bytes, file);
    
    fclose(file);

    return text;
}

我有这样一段代码,但如果我的 file 为以下文本:“我的名字是玛丽亚。我 19 岁。”第二个短语在开头印有''。有人可以帮忙找到一种方法来忽略这些空间吗?谢谢

回答1

首先,您有几个问题会调用https://en.cppreference.com/w/c/language/behavior。在

char *line = (char*) malloc(sizeof(text));

sizeof (text) 是指针 (char *) 的大小,而不是它指向的缓冲区的长度。

sizeof (char *) 取决于您的系统,但很可能是 8(如果您好奇,请继续测试:printf("%zu\n", sizeof (char *));),这意味着 line 可以保存长度为 7 的字符串(加上空终止字节)。

长句很容易溢出这个缓冲区,导致https://en.cppreference.com/w/c/language/behavior

(旁白:https://stackoverflow.com/a/605858/2505965 的返回值。)

此外,strlen(text) 可能无法正常工作,因为 text 可能不包含空终止字节 ('\0')。 fread 使用原始字节,并且不理解 https://en.cppreference.com/w/c/string/byte 的概念 - files 不必是 null-terminated,并且 fread 不会是 null-terminate为你缓冲。

您应该在 read_file 函数中分配一个额外的字节

text = malloc(num_bytes + 1);
text[num_bytes] = 0;

并将空终止字节放在那里。

(旁白:sizeof (char) 保证为 1。)

请注意,不应依赖 ftell 来确定 file https://stackoverflow.com/a/49122325/2505965

https://en.cppreference.com/w/c/string/byte/isspace 来自 <ctype.h> 可用于确定当前字符是否为空格。它的参数应该转换为 unsigned char。请注意,这将包括 '\t''\n' 等字符。如果您只关心空格 (text[i + 1] == ' '),请使用简单比较。

在匹配分隔符后,可以使用循环来消耗尾随空格。

确保在打印之前以空值终止 line,因为 %s 需要一个字符串。

使用 %u 打印 unsigned int

完成后不要忘记 free 动态分配的内存。此外,认真考虑检查任何可能失败的库函数都没有这样做。

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

void pdie(const char *msg) {
    perror(msg);
    exit(EXIT_FAILURE);
}

char *read_file(char *name) {
    FILE *file = fopen(name, "r");

    if (!file)
        pdie(name);

    fseek(file, 0, SEEK_END);
    long num_bytes = ftell(file);

    if (-1 == num_bytes)
        pdie(name);

    fseek(file, 0, SEEK_SET);

    char *text = malloc(num_bytes + 1);

    if (!text)
        pdie("malloc");

    if (-1 == num_bytes)
        pdie(name);

    text[num_bytes] = 0;

    if (fread(text, 1, num_bytes, file) != num_bytes)
        pdie(name);

    fclose(file);

    return text;
}

int main(int argc, char **argv) {
    if (argc < 2) {
        fprintf(stderr, "usage: %s TEXT_FILE\n", argv[0]);
        return EXIT_FAILURE;
    }

    char *text = read_file(argv[1]);
    unsigned int count = 0;

    size_t length = strlen(text);
    size_t index = 0;
    char *line = malloc(length + 1);

    if (!line)
        pdie("malloc");

    for (size_t i = 0; i < length; i++) {
        line[index++] = text[i];

        if (text[i] == '.' || text[i] == '?' || text[i] == '!') {
            line[index] = '\0';
            index = 0;

            printf("[%u] <<%s>>\n", ++count, line);

            while (isspace((unsigned char) text[i + 1]))
                i++;
        }
    }

    free(text);
    free(line);

    return EXIT_SUCCESS;
}

输入 file:

My name is Maria. I'm 19. Hello world! How are you?

stdout:

[1] <<My name is Maria.>>
[2] <<I'm 19.>>
[3] <<Hello world!>>
[4] <<How are you?>>

回答2

您可以通过将相关字符与“ ”进行比较来测试空格字符。

if(text[i] == ' ')
    // text[i] is whitespace

回答3

一种可能的解决方案是,当您找到句子的结尾时,前进到下一个非空白字符。您还需要确保 malloc 有足够的内存用于当前短语:

#include <ctype.h>  // for isspace
... 

size_t textLength = strlen(text);
// malloc based on the text length here, plus 1 for the NUL terminator.
// sizeof(text) gives you the size of the pointer, not the size of the
// memory block it points to.
char *line = malloc(textLength+1);

for(size_t i = 0; i < textLength; i++) {
    line[index] = text[i];
    index++;
    if(text[i] == '.' || text[i] == '?' || text[i] == '!') {
        count++;
        printf("[%d] %s\n", count, line);
        memset(line, 0, index + 1);
        index = 0;
        // advance to the next non-whitespace char
        do
        {
            // advance to the next char (we know the current char is not a space)
            i++;
        // keep advancing i while the next char is in range of the
        // text and the next char is a space.
        }while (i+1 < textLength && isspace(text[i+1]) != 0);
    }
}

输出:

[1] My name is Maria.
[2] I'm 19.

https://godbolt.org/z/xMh93K737

还有 https://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc

相似文章

c - 在 C 中绘制镜像箭头

我无法使用星号在C中绘制此形状:**************************************************我知道如何制作右箭头或左箭头,但我不知道如何制作这样的箭头。这是我到...

linux - 从 Centos7/8 迁移到 AlmaLinux 后丢失 dnf

我将CentOS7迁移到8,然后迁移到AlmaLinux,但在此过程中我丢失了dnf和yum。现在我尝试修复,但在依赖项等方面存在很多问题。只有rpm正在工作,所以我必须“手动”完成所有工作。如果我尝...

windows - pgAdmin4 升级到 6.9 问题

以前,我在我的机器上安装了pgAdmin45.2版,今天我从网站下载了pgAdmin46.9版。它安装正确,没有错误,但最新版本的应用程序没有在我的机器上打开。以下是启动画面消失后我得到的错误信息:新...

最新文章