В основном мой входной файл имеет формат:
I 15 3 15 10 10 20
S -5 3 15 82
I -20 80 -4 10
S 4 -20 8
Количество целых чисел в строке может варьироваться, но в начале каждой строки всегда есть один символ.
На основании символьного значения «I» или «S» я вставляю или ищу заданные целые числа в соответствующей строке. Поскольку нет условия EOL, подобного EOF, как мне остановиться в конце строки? В идеале я хотел бы использовать fscanf.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
void readFileLines(char** argv)
{
FILE* fp = fopen(argv[1], "r");
char read;
while(fscanf(fp, "%c\t", &read) != EOF)
{
//scan first letter in line until EOF
if (read == 'I')
{
//loop through all values in Line
int data;
printf("Inputting...");
while(fscanf(fp, "%i\t", &data) != EOL) //ERROR (EOL DOESNT WORK)
{
//iterate rest of line values til end of line
printf("%d\t", data);
}
}
else if (read == 'S')
{
int data;
printf("Searching...");
while(fscanf(fp, "%d\t", &data) != EOL) // ERROR (EOL DOESNT WORK)
{
printf("%d\t", data);
}
}
printf("\n");
//iterate through to next line in order to scan different letteer
}
}
int main(int argc, char** argv)
{
readFileLines(argv);
return EXIT_SUCCESS;
}
Я слышал, что fgets
может быть полезен в этом сценарии, если использовать \n
в конце каждой строки как способ указать, когда строка заканчивается, но я не совсем уверен, как работает этот метод. Пожалуйста, дайте мне знать!
При условии, что fgets()
передается достаточно большой массив, он прочитает целую строку и включит '\n'
в качестве последнего символа в строку. Затем вы можете игнорировать или стереть его. char line[1000]; while (fgets(line, sizeof line, stdin)) { /* deal with line */ } /* all lines dealt with now */
Я не понимаю следующее предложение: Based off the char value, 'I' or 'S', I insert or search for the given integers in that respective row.
-- Что именно вы хотите сделать, когда "вставляете"? Что именно вы хотите сделать по-другому в зависимости от того, первая буква — 'I'
или 'S'
? Единственная разница в том, что вы хотите напечатать другую строку, в зависимости от того, 'I'
это или 'S'
?
@Axnyff Когда fscanf
вернется \n
? Вы, наверное, имеете в виду != 1
.
Я слышал, что fgets может быть полезен в этом сценарии, используя \n в конце каждой строки как способ указать, когда строка заканчивается, но я не совсем уверен, как работает этот метод.
fgets()
, иногда в сочетании с sscanf()
, довольно широко рекомендуется как превосходящий fscanf()
, особенно для линейного ввода. Вы можете найти документацию по нему онлайн в разных местах.
Но в вашем конкретном случае вам не обязательно рассматривать вводимые данные как серию строк. Вместо этого вы можете рассматривать его как последовательность отдельных входных элементов, разделенных пробелами, некоторые из которых являются числами, а другие - символами 'I'
или 'S'
. Чтение по одному полю за раз через fscanf
здесь довольно доступно, при этом обсуждается множество fscanf
ловушек, и у вас не будет недостатка в беспокойстве о длине строки. Здесь вам хотелось бы лучше воспользоваться возвращаемым значением fscanf()
, которое не только сигнализирует об окончании ввода, но и в других случаях сообщает вам, сколько входных элементов было успешно отсканировано и преобразовано.
Таким образом, вы можете сделать что-то вроде этого:
char c;
// expecting I or S:
int num_items = fscanf(fp, " %c", &c);
if (num_items == 1) {
// successfully read an 'I' or an 'S' ...
} else if (num_items == EOF) {
// end of input ...
} else {
// invalid input ...
}
... а вот так:
// expecting numbers:
// loop:
int num;
num_items = fscanf(fp, "%d", &num);
if (num_items == 1) {
// successfully read one number ...
} else if (num_items == EOF) {
// end of input ...
} else {
// Probably an 'I' or 'S' (which remains unread) -- end of record ...
}
Большое спасибо, это действительно меня выручило!
Я слышал, что
fgets
может быть полезен в этом сценарии, если использовать\n
в конце каждой строки как способ указать, когда строка заканчивается, но я не совсем уверен, как работает этот метод.
Я предлагаю вам создать свою собственную функцию, которая одновременно считывает одну строку ввода, используя fgets
, а затем другую функцию, которая обрабатывает эту строку ввода. Затем вы можете вызывать эти функции в цикле.
Хотя вы можете использовать функцию sscanf для обработки ввода, эта функция имеет тот недостаток, что неопределенное поведение будет вызвано, если число выходит за пределы диапазона целевого типа данных. По этой причине я рекомендую вместо этого использовать strtol, который позволяет обнаружить такую ситуацию и вывести соответствующее сообщение об ошибке вместо вызова неопределенного поведения.
Вот пример:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>
#include <errno.h>
bool get_line_from_stream( char buffer[], int buffer_size, FILE *fp );
bool process_line( char line[] );
int main( int argc, char *argv[] )
{
FILE* fp;
char line[200];
// check for correct number of command-line arguments
if ( argc != 2 )
{
fprintf( stderr, "Please specify an input file!\n" );
exit( EXIT_FAILURE );
}
// open input file
fp = fopen( argv[1], "r" );
if ( fp == NULL )
{
fprintf( stderr, "Error opening file!\n" );
exit( EXIT_FAILURE );
}
// read the input file line by line and process the
// individual lines, one line per loop iteration
while ( get_line_from_stream( line, sizeof line, fp ) )
{
if ( ! process_line( line ) )
break;
}
// cleanup
fclose( fp );
}
bool process_line( char line[] )
{
// process the first character of the line
switch( line[0] )
{
case 'I':
printf( "Inputting..." );
break;
case 'S':
printf( "Searching..." );
break;
default:
fprintf( stderr, "Error reading input!\n" );
return false;
}
// process the remainder of the line
for ( char *p = &line[1]; ; ) // infinite loop
{
long num;
char *q;
// convert one number
errno = 0;
num = strtol( p, &q, 10 );
// test whether conversion was successful
if ( p == q )
{
// skip all whitespace characters
while ( isspace( (unsigned char)*p ) )
p++;
// test whether we successfully reached the end of
// the line
if ( *p == '\0' )
{
printf( "\n" );
return true;
}
fprintf( stderr, "Encountered invalid character!\n" );
return false;
}
// test whether range error occurred
if ( errno == ERANGE )
{
fprintf(
stderr,
"Could not convert character due to range error!\n"
);
return false;
}
// the character was successfully converted, so print it
printf( " %ld", num );
// make p point to the first non-converted character
p = q;
}
}
//This function will read exactly one line of input and remove the
//newline character, if it exists. On success, it will return true.
//If this function is unable to read any further lines due to
//end-of-file, it will return false. If it fails for any other reason,
//it will not return, but will print an error message and call "exit"
//instead.
bool get_line_from_stream( char buffer[], int buffer_size, FILE *fp )
{
char *p;
//attempt to read one line from the stream
if ( fgets( buffer, buffer_size, fp ) == NULL )
{
if ( !feof(fp) )
{
fprintf( stderr, "Input error!\n" );
exit( EXIT_FAILURE );
}
return false;
}
//make sure that line was not too long for input buffer
p = strchr( buffer, '\n' );
if ( p == NULL )
{
//a missing newline character is ok if the next
//character is a newline character or if we have
//reached end-of-file
if ( getc(fp) != '\n' && !feof(fp) )
{
fprintf( stderr, "Line is too long for memory buffer!\n" );
exit( EXIT_FAILURE );
}
}
else
{
//remove newline character by overwriting it with a null
//character
*p = '\0';
}
return true;
}
Для ввода, указанного в вопросе, эта программа имеет следующий вывод:
Inputting... 15 3 15 10 10 20
Searching... -5 3 15 82
Inputting... -20 80 -4 10
Searching... 4 -20 8
Мне нравится ответ Джона, при условии, что входной файл находится под строгим контролем и не имеет реальной возможности содержать недопустимый контент. Т.е. входной файл создается какой-то другой программой, которая никогда не выводит мусор. John’s уловит множество неверных данных, но вы можете сделать больше.
Однако, если входной файл может быть введен человеком или программой, в которой может быть ошибка, вам действительно следует выполнить больше проверок и отчетов об ошибках, что практически невозможно сделать с помощью fscanf.
Поскольку ваш ввод основан на строках, я бы предложил использовать fgets()
для чтения строк. Затем используйте strtok()
для анализа полей. Затем используйте strtol()
, чтобы преобразовать целые числа в длинные, которые следует проверить по диапазону, прежде чем рассматривать как целые. (Вот почему я использую strtol()
.)
Вот мой код. Я также принял пробелы и табуляции в качестве разделителей и разрешил пустые строки ввода. Я думаю, что он хорошо справляется с проверкой ввода.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main(int argc, char **argv) {
char iline[256];
int line_num = 0;
while (fgets(iline, sizeof(iline), stdin) != NULL) {
line_num++;
char *token;
token = strtok(iline, " \t\n");
if (token == NULL) {
continue; // Ignore blank lines.
}
if (strcmp(token, "S") == 0) {
printf("S"); // Process 'S'.
}
else if (strcmp(token, "I") == 0) {
printf("I"); // Process 'I'.
}
else {
printf("Error line %d, invalid prefix: '%s'\n", line_num, token);
continue; // Should return error; this is only for testing.
}
token = strtok(NULL, " \t\n");
while (token != NULL) { // Process each of the numeric fields.
char *p = NULL;
errno = 0;
long val = strtol(token, &p, 10);
if (errno != 0 || p == token || p == NULL || *p != '\0') {
printf("Error line %d, invalid number: '%s'\n", line_num, token);
break; // Should return error; this is only for testing.
}
printf(",%ld", val);
token = strtok(NULL, " \t\n");
}
printf("\n");
}
return 0;
}
Мне любопытно: какой тип недопустимого ввода, по вашему мнению, будет обрабатываться лучше с помощью этого подхода, чем тот, который я обрисовал?
Я не пробовал ваш код, но думаю, что «SIS1 2 3» будет интерпретироваться как S с нулевыми целыми числами, I с нулевыми целыми числами, S с 1, 2, 3. Что, возможно, нормально принять. Но я предпочитаю иметь четко определенный синтаксис ввода и применять его. Кроме того, этот метод не позволит вам напечатать сообщение с номером строки, вызывающим нарушение (который, конечно, я не включил).
Если вы замените свое внутреннее состояние
!= EOL
на!= '\n'
, это поможет? возможно, вам нужно проверить как EOL, так и символ новой строки, и прервать основной цикл, если вы получите символ EOL