我需要打印 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