Прохождение текстового файла по строке в C
Я работал над небольшим упражнением для своего класса CIS и очень смущен методами, которые использует C для чтения из файла. Все, что мне действительно нужно сделать, это прочитать файл по строкам и использовать информацию, собранную из каждой строки, чтобы сделать несколько манипуляций. Я пробовал использовать метод getline и другие без везения.
Мой код в настоящее время выглядит следующим образом:
int main(char *argc, char* argv[]){
const char *filename = argv[0];
FILE *file = fopen(filename, "r");
char *line = NULL;
while(!feof(file)){
sscanf(line, filename, "%s");
printf("%s\n", line);
}
return 1;
}
Сейчас я получаю seg-ошибку с помощью метода sscanf, и я не уверен, почему. Я полный C noob и просто задаюсь вопросом, есть ли какая-то большая картина, которую я отсутствовал.
Благодаря
Ответы
Ответ 1
Так много проблем в стольких строках. Вероятно, я забыл:
- argv [0] - это имя программы, а не первый аргумент;
- Если вы хотите прочитать переменную, вам нужно выделить ее память
- никто никогда не контактирует с feof, один цикл на функции ввода-вывода до тех пор, пока он не сработает, feof затем служит для определения причины сбоя,
- sscanf используется для разбора строки, если вы хотите проанализировать файл, используйте fscanf,
- "% s" остановится в первом пространстве как формат семейства? scanf
- чтобы прочитать строку, стандартная функция - fgets,
- возврат 1 из отказа основного средства
Итак,
#include <stdio.h>
int main(int argc, char* argv[])
{
char const* const fileName = argv[1]; /* should check that argc > 1 */
FILE* file = fopen(fileName, "r"); /* should check the result */
char line[256];
while (fgets(line, sizeof(line), file)) {
/* note that fgets don't strip the terminating \n, checking its
presence would allow to handle lines longer that sizeof(line) */
printf("%s", line);
}
/* may check feof here to make a difference between eof and io failure -- network
timeout for instance */
fclose(file);
return 0;
}
Ответ 2
Чтобы прочитать строку из файла, вы должны использовать функцию fgets
: она читает строку из указанного файла до символа новой строки или EOF
.
Использование sscanf
в вашем коде не будет работать вообще, поскольку вы используете filename
как строку формата для чтения из line
в константный строковый литерал %s
.
Причиной SEGV является то, что вы записываете в неориентированную память, на которую указывает line
.
Ответ 3
В дополнение к другим ответам в недавней библиотеке C (совместимый с Posix 2008) вы можете использовать getline. См. этот ответ (к соответствующему вопросу).
Ответ 4
Предположим, что вы имеете дело с каким-либо другим разделителем, например с вкладкой \t
вместо строки \n
.
Более общий подход к разделителям - это использование getc()
, которое захватывает по одному символу за раз.
Обратите внимание, что getc()
возвращает int
, поэтому мы можем проверить равенство с EOF
.
Во-вторых, мы определяем массив line[BUFFER_MAX_LENGTH]
типа char
для хранения до BUFFER_MAX_LENGTH-1
символов в стеке (мы должны сохранить этот последний символ для символа терминатора \0
).
Использование массива позволяет избежать необходимости использовать malloc
и free
для создания указателя символа нужной длины в куче.
#define BUFFER_MAX_LENGTH 1024
int main(int argc, char* argv[])
{
FILE *file = NULL;
char line[BUFFER_MAX_LENGTH];
int tempChar;
unsigned int tempCharIdx = 0U;
if (argc == 2)
file = fopen(argv[1], "r");
else {
fprintf(stderr, "error: wrong number of arguments\n"
"usage: %s textfile\n", argv[0]);
return EXIT_FAILURE;
}
if (!file) {
fprintf(stderr, "error: could not open textfile: %s\n", argv[1]);
return EXIT_FAILURE;
}
/* get a character from the file pointer */
while(tempChar = fgetc(file))
{
/* avoid buffer overflow error */
if (tempCharIdx == BUFFER_MAX_LENGTH) {
fprintf(stderr, "error: line is too long. increase BUFFER_MAX_LENGTH.\n");
return EXIT_FAILURE;
}
/* test character value */
if (tempChar == EOF) {
line[tempCharIdx] = '\0';
fprintf(stdout, "%s\n", line);
break;
}
else if (tempChar == '\n') {
line[tempCharIdx] = '\0';
tempCharIdx = 0U;
fprintf(stdout, "%s\n", line);
continue;
}
else
line[tempCharIdx++] = (char)tempChar;
}
return EXIT_SUCCESS;
}
Если вы должны использовать char *
, вы все равно можете использовать этот код, но вы strdup()
массив line[]
, как только он будет заполнен линией ввода. Вы должны free
выполнить эту дублируемую строку, как только вы закончите с ней, или вы получите утечку памяти:
#define BUFFER_MAX_LENGTH 1024
int main(int argc, char* argv[])
{
FILE *file = NULL;
char line[BUFFER_MAX_LENGTH];
int tempChar;
unsigned int tempCharIdx = 0U;
char *dynamicLine = NULL;
if (argc == 2)
file = fopen(argv[1], "r");
else {
fprintf(stderr, "error: wrong number of arguments\n"
"usage: %s textfile\n", argv[0]);
return EXIT_FAILURE;
}
if (!file) {
fprintf(stderr, "error: could not open textfile: %s\n", argv[1]);
return EXIT_FAILURE;
}
while(tempChar = fgetc(file))
{
/* avoid buffer overflow error */
if (tempCharIdx == BUFFER_MAX_LENGTH) {
fprintf(stderr, "error: line is too long. increase BUFFER_MAX_LENGTH.\n");
return EXIT_FAILURE;
}
/* test character value */
if (tempChar == EOF) {
line[tempCharIdx] = '\0';
dynamicLine = strdup(line);
fprintf(stdout, "%s\n", dynamicLine);
free(dynamicLine);
dynamicLine = NULL;
break;
}
else if (tempChar == '\n') {
line[tempCharIdx] = '\0';
tempCharIdx = 0U;
dynamicLine = strdup(line);
fprintf(stdout, "%s\n", dynamicLine);
free(dynamicLine);
dynamicLine = NULL;
continue;
}
else
line[tempCharIdx++] = (char)tempChar;
}
return EXIT_SUCCESS;
}