Обработка файлов с помощью текста - PullRequest
0 голосов
/ 23 мая 2019

Я читаю файл с текстом и целым числом в нем. Нужно извлечь только целое число, пропуская текст

Я реализовал код, читающий целые числа, но как пропустить промежуточный текст и продолжить чтение целого числа.

Ввод:

01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
some text
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
#include<bits/stdc++.h>
using namespace std;
int main(void){
 unsigned int number1,number2,number3,number4; 

          FILE* in_file = fopen("example.txt", "r"); 
           FILE* in_file1 = fopen("wrte.txt", "w"); 
           if (! in_file ) 
             {  
                printf("oops, file can't be read\n"); 
                exit(-1); 
             } 

          // attempt to read the next line and store 
          // the value in the "number" variable 

          while (fscanf(in_file,"%08x", &number1) == 1){ 
                fprintf(in_file1,"%08x\n", number1); 
             }
    fclose(in_file1);
    fclose(in_file);
return 0;
}

Expected output : Each 01000000 in a single line without text in it

Ответы [ 5 ]

1 голос
/ 24 мая 2019

Я могу придумать пару простых способов сделать это.

Вы можете прочитать все как string, а затем отбросить все, что не может быть преобразовано в целое число.

std::string token;
while (filein >> token) // read string, exit on failure
{
    try 
    {
        int value = std::stoul(token, 0, 16); // try to turn string into integer
        use value        
    }
    catch (const std::invalid_argument &) // couldn't convert. do nothing
    {
    }
}

Документация для std::stoul.

Если нечисловые данные встречаются часто (например, они не являются исключительными), вы можете не захотеть использовать исключения. В этом случае обратите внимание на использование strtoul и выполнение обработки ошибок самостоятельно.

Документация для strtoul.

Вы также можете использовать >>, чтобы читать целые числа как целые и проверять успешность. Когда чтение не удается, clear бит сбоя и читается как string. Если вы не можете прочитать string, файл поврежден или полностью уничтожен. Хватит читать. Если вы можете прочитать string, выбросьте его и вернитесь к чтению целых чисел

while (true)
{
    int value;
    if (filein >> std::hex >> value) // read a number
    {
        use value
    }
    else // failed to read number
    {
        filein.clear(); // clear fail bit
        std::string junk;
        if (!(filein >> junk)) // read a string
        {
            break; // no more readable data. exit loop
        }
         // do nothing with junk.
    }
}

Вы можете улучшить это с помощью ignore, проверив конец файла и выйдя перед попыткой прочитать string

Документация для std::stoul.

0 голосов
/ 24 мая 2019

Вот читабельное решение с акцентом на простоту:

// -*- compile-command: "g++ data.cpp; ./a.out "; -*-
#include <fstream>
#include <iomanip>
#include <iterator>
#include <string>

int main()
{
  std::ifstream fin("data.in");
  std::ofstream fout("data.out");

  auto fin_iter = std::istream_iterator<std::string>(fin);
  const auto fin_iter_end = std::istream_iterator<std::string>();

  while (fin_iter != fin_iter_end)
  {
    try
    {
      fout << std::setfill('0') << std::setw(8) << std::stoul(*fin_iter) << " ";
    }
    catch (...)
    {
    }
    ++fin_iter;
  };

  fin.close();
  fout.close();

  return 0;
}

Вот "идея":

Чтобы ответить на ваш вопрос, вы можете «пропустить текст, заключенный между целыми числами», так как в этом случае stoul выдает исключение. Если мы ловим исключение, мы ничего не делаем, в противном случае мы записываем преобразованное целое число в выходной файл.

data.in

01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
some text
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000

data.out

01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000
0 голосов
/ 24 мая 2019

Конечно, это довольно простая задача, вам просто нужно немного изменить цикл и использовать feof в качестве условия цикла:

while(feof(in_file) == 0) {
    if(fscanf(in_file, " %8x ", &number1) > 0) {
        fprintf(in_file1,"%08x\n", number1);
    } else {
        fscanf(in_file, " %*s ");
    }
}

Живой пример

Я также хотел бы предложить вам отказаться от FILE* и начать использовать fstreams в , но это всего лишь предложение по удобству.

0 голосов
/ 24 мая 2019

Хотя вы обычно используете библиотеку C ++ iostream для файлового ввода / вывода, нет ничего, что говорило бы о том, что вы не можете использовать функции C cstdio, такие как fscanf - при условии, что вы используете их правильно (и часто они будут быстрее, чем iostream подход)

В вашем случае у вас много цифр с текстом в середине, которые вы пытаетесь прочитать с помощью fscanf в цикле. Это нормально, это достаточно просто сделать, но ... вы должны правильно обработать сбой сопоставления случай , который произойдет, когда вы попытка чтения 's' с помощью спецификатора преобразования "%08x" .

Когда совпадает с ошибкой , извлечение символа из потока останавливается в точке сбоя, оставляя все, начиная с символа, вызывающего сбой (и что следует за ним) непрочитанным во входном буфере. Если вы правильно не извлечете символы, вызывающие сбой совпадения из входного потока, вы, скорее всего, столкнетесь с бесконечным циклом, поскольку символы, вызывающие сбой, остаются непрочитанными, просто ожидая, чтобы вас снова укусили при следующей попытке чтения.

Итак, как справиться с ошибкой совпадения ? Заголовок cctype предоставляет макрос isdigit, который позволяет просто проверить, является ли следующий символ во входном потоке цифрой. Вы проверяете символ, сначала читая с fgetc (или getc - то же самое, но часто реализуемое как макрос), а затем с isdigit, например,

            int c = fgetc(in_file);             /* read next char */
            while (c != EOF && !isdigit(c))     /* check EOF and isdigit */
                c = fgetc(in_file);             /* get next char */

Выше вы читаете следующий символ, затем вводите цикл, подтверждающий, что вы не достигли EOF, и затем проверяете, является ли c Не цифрой. Если эти условия выполняются, вы снова проверяете следующий символ, пока не достигнете EOF Или не найдете следующую цифру во входном потоке. Но теперь у вас есть проблема, вы уже прочитали цифру из потока, как fscanf сможет прочитать ее как часть следующего целого числа?

Простой - положить его обратно в поток ввода:

            if (c != EOF)                       /* if not EOF, then digit */
                ungetc (c, in_file);            /* put back for next read */

Теперь вы можете прочитать все 64 целочисленных значения из in_file с помощью простого цикла, например,

    while (1) { /* loop continually until EOF */
        int rtn = fscanf (in_file,"%08x", &number1);    /* validate return */
        if (rtn == EOF)         /* if EOF, break loop */
            break;
        else if (rtn == 0) {    /* handle matching failure */
            int c = fgetc(in_file);             /* read next char */
            while (c != EOF && !isdigit(c))     /* check EOF and isdigit */
                c = fgetc(in_file);             /* get next char */
            if (c != EOF)                       /* if not EOF, then digit */
                ungetc (c, in_file);            /* put back for next read */
        }
        else    /* good read, output number */
            fprintf (out_file, "%08x\n", number1); 
    }

( примечание: ваш выходной файл был переименован из in_file1 в out_file - всегда используйте значимые имена переменных)

Теперь некоторые убираются. Когда вы открываете in_file, вы подтверждаете, что файл открыт для чтения. Хорошо, но для условия ошибки вы exit (-1);. Не возвращайте отрицательные значения в оболочку. У вас есть две константы для обозначения имен успехов / неудач EXIT_SUCCESS (0) и EXIT_FAILURE (значение 1, не -1).

Хотя вы проверяли, что in_file был открыт для чтения, вы не смогли проверить, открыт ли ваш выходной файл для записи? Всегда проверяйте возврат всех потоков ввода / вывода и функций ввода / вывода. В противном случае попытка записи в поток в состоянии ошибки вызывает неопределенное поведение.

В целом, вы можете сделать:

#include <cstdio>
#include <cstdlib>
#include <cctype>

using namespace std;

int main (void) {

    unsigned int number1; 

    FILE* in_file = fopen ("example.txt", "r"); 
    FILE* out_file = fopen ("wrte.txt", "w"); 

    if (!in_file) {     /* validate file open for reading */
        printf ("oops, file can't be read\n"); 
        exit (1);       /* don't return negative values to the shell */
    }
    if (!out_file) {    /* validate file open for writing */
        printf ("oops, file can't be read\n"); 
        exit (1);       /* don't return negative values to the shell */
    }

    while (1) { /* loop continually until EOF */
        int rtn = fscanf (in_file,"%08x", &number1);    /* validate return */
        if (rtn == EOF)         /* if EOF, break loop */
            break;
        else if (rtn == 0) {    /* handle matching failure */
            int c = fgetc(in_file);             /* read next char */
            while (c != EOF && !isdigit(c))     /* check EOF and isdigit */
                c = fgetc(in_file);             /* get next char */
            if (c != EOF)                       /* if not EOF, then digit */
                ungetc (c, in_file);            /* put back for next read */
        }
        else    /* good read, output number */
            fprintf (out_file, "%08x\n", number1); 
    }
    fclose (in_file);
    fclose (out_file);
}

Пример выходного файла

$ cat wrte.txt
01000000
01000000
01000000
01000000
...
01000000

Записаны все 64 значения, которые можно подтвердить с помощью wc -l, например,

$ wc -l < wrte.txt
64

Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы. Та же логика применима, если вы используете библиотеку iostream, имена функций немного отличаются (некоторые идентичны), но вместо этого реализованы как функции-члены.

0 голосов
/ 23 мая 2019

Я почти никогда не полагаюсь на такие вещи, как fscanf.

Я бы читал строки текста и разбирал их разумно. Если вы знаете, что строка разделена пробелами, вы можете разделить пробелы, а затем посмотреть на каждый фрагмент по-своему. if (isdigit (первый символ в чанке)), тогда int value = atoi (чанк).

Вы можете даже быть более осторожным и убедиться, что весь кусок представляет собой допустимый номер, прежде чем звонить в atoi.

...