Чтение отформатированных строк из файла в массив в C - PullRequest
1 голос
/ 12 февраля 2012

Я новичок в языке программирования C и пытаюсь улучшить его, решая проблемы с веб-сайта Project Euler, используя только C и его стандартные библиотеки.Я рассмотрел основные основы языка Си (я думаю), функции, указатели и некоторые основные операции ввода-вывода файлов, но сейчас сталкиваюсь с некоторыми проблемами.

Вопрос заключается в чтении текстового файла с именами и вычислении «имени».оценка "бла-бла, я знаю алгоритм, который собираюсь использовать, и имею большую часть настроек программы, но просто не могу понять, как правильно читать файл.

Файл имеет формат "Nameone", "Nametwo", "billy", "bobby", "frank" ... Я искал и искал и пробовал бесчисленные вещи, но не могу прочитать их какотдельные имена в массив строк (я думаю, это правильный способ хранить их по отдельности?) Я попытался использовать sscanf / fscanf с% [^ \ ",]. Я пробовал разные комбинации этих функций и fgets, но мое пониманиеиз fgets каждый раз, когда я его называю, он получает новую строку, и это текстовый файл, содержащий более 45 000 символов в одной строке.

Я не уверен, что у меня возникнут проблемы с неправильным пониманиемфункции scanf, или мое недоразумение с сохранением массива строк. Что касается массива строк, я (думаю) осознал, что когда я объявляю массив строк, он не выделяет память для самих строк, то, что янужно сделать. Но я все еще не могу заставить что-либо работать.

Вот код, который я сейчас пытаюсь просто прочитать под некоторыми именами, которые яnter из командной строки, чтобы проверить мои методы.

Этот код работает для ввода любой строки до размера буфера (100):

int main(void)
{
   int i;
   char input[100];
   char* names[10];

   printf("\nEnter up to 10 names\nEnter an empty string to terminate input: \n");

   for(int i = 0; i < 10; i++)
   {
      int length = 0;
      printf("%d: ", i);
      fgets(input, 100, stdin);
      length = (int)strlen(input);
      input[length-1] = 0;        // Delete newline character
      length--;

      if(length < 1)
      {
         break;
      }

      names[i] = malloc(length+1);
      assert(names[i] != NULL);
      strcpy(names[i], input);
   }
}

Однако я просто не могу заставить эту работу читатьв отформатированных строках.

ПОЖАЛУЙСТА, посоветуйте мне, как читать в формате.Ранее я использовал sscanf во входном буфере, и это работало нормально, но я не чувствую, что могу сделать это на 45000+ символьных строках?Я прав, предполагая это?Является ли это даже приемлемым способом чтения строк в массив?

Я извиняюсь, если это долго и / или неясно, очень поздно, и я очень расстроен.

Спасибо всем ивсем за помощь, и я с нетерпением жду, чтобы наконец стать активным участником на этом сайте!

Ответы [ 3 ]

1 голос
/ 12 февраля 2012

Здесь действительно есть две основные проблемы:

  1. Является ли сканирование строкового ввода правильной стратегией здесь.Я бы не стал спорить, потому что, хотя он может работать над этой задачей, вы столкнетесь с более сложными сценариями, в которых он слишком легко ломается.
  2. Как обрабатывать строку из 45 тыс.на самом деле вы не столкнетесь с большим количеством строк такого размера, но это не то, с чем современный компьютер любой емкости не может легко справиться.Поскольку это делается для целей обучения, учитесь итеративно.

    Самый простой первый подход - это fread() всей строки / файла в буфер соответствующего размера и синтаксический анализ его самостоятельно.Вы можете использовать strtok(), чтобы разбить токены, разделенные запятыми, а затем передать токены функции, которая удаляет кавычки и возвращает слово.Добавьте слово в ваш массив.

    За второй проход вы можете покончить с strtok() и просто разобрать строку самостоятельно, перебирая буфер и разбивая токены запятой самостоятельно.

    И последнее, но не менее важное: вы можете написать версию, которая считывает небольшие фрагменты файла в меньший буфер и анализирует их.Это имеет дополнительную сложность обработки нескольких операций чтения и управления буферами для учета токенов половинного чтения в конце буфера и т. Д.

    В любом случае разбивайте задачу на куски и изучайте с каждым уточнением.

    РЕДАКТИРОВАТЬ

    #define MAX_STRINGS 5000
    #define MAX_NAME_LENGTH 30
    
    char* stripQuotes(char *str, char *newstr)
    {
        char *temp = newstr;
    
        while (*str)
        {
            if (*str != '"')
            {
               *temp = *str;
               temp++;
            }
    
            str++;
        }
    
        return(newstr);
    }
    
    int main(int argc, char *argv[])
    {
        char  fakeline[] = "\"Nameone\",\"Nametwo\",\"billy\",\"bobby\",\"frank\"";
        char *token;
        char  namebuffer[MAX_NAME_LENGTH] = {'\0'};
        char *name;
         int  index = 0;
        char  nameArray[MAX_STRINGS][MAX_NAME_LENGTH];
    
        token = strtok(fakeline, ",");
        if (token)
        {
            name = stripQuotes(token, namebuffer);
            strcpy(nameArray[index++], name);
        }
    
        while (token != NULL)
        {
            token = strtok(NULL, ",");
    
            if (token)
            {
                memset(namebuffer, '\0', sizeof(namebuffer));
                name = stripQuotes(token, namebuffer);
                strcpy(nameArray[index++], name);
            }
        }
    
        return(0);
    }
    
0 голосов
/ 12 февраля 2012

Почему бы не искать в гигантской строке символы кавычек? Примерно так:

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

int main(void)
{
    char mydata[] = "\"John\",\"Smith\",\"Foo\",\"Bar\"";
    char namebuffer[20];

    unsigned int i, j;
    int begin = 1;
    unsigned int beginName, endName;
    for (i = 0; i < sizeof(mydata); i++)
    {
        if (mydata[i] == '"')
        {
            if (begin)
            {
                beginName = i;
            }
            else
            {
                endName = i;
                for (j = beginName + 1; j < endName; j++)
                {
                    namebuffer[j-beginName-1] = mydata[j];
                }
                namebuffer[endName-beginName-1] = '\0';
                printf("%s\n", namebuffer);
            }
            begin = !begin;
        }
    }
}

Вы находите первую двойную кавычку, затем вторую, а затем зачитываете символы между строкой вашего имени. Затем вы обрабатываете эти символы по мере необходимости для рассматриваемой проблемы.

0 голосов
/ 12 февраля 2012

fscanf("%s", input) читает один токен (строку, окруженную пробелами) за раз.Вы можете либо сканировать ввод, пока не встретите конкретную строку «конец ввода», например «!», Либо дождаться сигнала конца файла, который достигается нажатием «Ctrl + D» наконсоль Unix или нажав «Ctrl + Z» на консоли Windows.

Первый параметр:

fscanf("%s", input);
if (input[0] == '!') {
    break;
}
// Put input on the array...

Второй параметр:

result = fscanf("%s", input);
if (result == EOF) {
    break;
}
// Put input on the array...

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

...