LINUX.ORG.RU

Как считать из файла набор строк с числами для сортировки этих строк по столбцу?

 


1

1

Файл состоит из строк вида: 11:22:33 9 value 20 техт

Нужно считать и по 4 колонке сортировать строки. Пробую так:

FILE * f = fopen("log.txt","r");
        if(!f)
            printf("Error open file\n");
        else
        {
            while(!feof(f))
            {
                char * ptr_str = fgets(buf,sizeof(buf),f);


                if( ptr_str != NULL)
                {
                    printf("\n string=%s",buf);

                }
                else
                {
                    printf("\n string null!");
                }
            }
            fclose(f);

        }
Как дальше из buf вытащить 4 столбик для сортировки.



Последнее исправление: beastie (всего исправлений: 3)

Ну, во-первых можно fscanf использовать. Ну а если надо без этого, то в цикле просто пройтись и посимвольно парсить

SZT ★★★★★
()
Ответ на: комментарий от anonymous

Не пашет почему-то sort на этой реализации линукс-девайса, поэтому ручками пишу.

user2132
() автор топика
Ответ на: комментарий от user2132

fscanf справится если между столбиками произвольное число пробелов ?

man 3 fscanf почитай.

A sequence of white-space characters (space, tab, newline, etc.; see isspace(3)). This directive matches any amount of white space, including none, in the input.

Т.е. один пробел матчит любое количество пробелов.

SZT ★★★★★
()
Ответ на: комментарий от SZT

sscanf пробую так sscanf(buf,«%d»,&val); но он берет от начала строки число, у меня в начале идет типа 10:20:30, которые надо пропустить, как sscanf указать это, чтобы она не хватала числа, после которых не пробел идет.

user2132
() автор топика

Загружаешь через QFile
Читаешь через readLine в структуру и сохраняешь в вектор
Потом сортируешь через std::sort, передав в него компаратор, который сравнивает по нужному полю структуры.
Пишется легко, читается просто, в отличии от той каши, которую ты нахерачишь на своей сишечке.

ox55ff ★★★★★
()
Ответ на: комментарий от ox55ff

Мне это надо в линукс девайс засунуть где всего-то 4 мб флеша, именно поэтому на си чистом, а на qt да это все легко делается.

user2132
() автор топика
Ответ на: комментарий от user2132

Ну почитай еще немного man 3 sscanf

Each conversion specification in format begins with either the character '%' or the character sequence «%n$» (see below for the distinction) followed by:

An optional '*' assignment-suppression character: scanf() reads input as directed by the conversion specification, but discards the input. No corresponding pointer argument is required, and this specification is not included in the count of successful assignments returned by scanf().

Вот например есть у тебя 11:22:33 9 value 20 техт строка и надо тебе оттуда достать 20. Звездочка * делает так, чтоб оно это матчило, но никуда не записывало. Ну и можно сделать fscanf с "%*d:%*d:%*d %*s %*s %d %*s\n"

Вот смотри пример https://wandbox.org/permlink/e6lin6M6rPVzWwzd

SZT ★★★★★
()
Ответ на: комментарий от Deleted

Думаю что теги gcc и clang будут лишними т.к. ни о каких специфичных для gcc или clang вещах тут не спрашивается.

SZT ★★★★★
()
Ответ на: комментарий от SZT

А можете еще со структурами подсказать, хочу строки из файла сохранить в структуру, поля структуры это столбцы строк будут. Заранее не знаю сколько строк в файле и создаю структуру динамически типа так:

typedef struct
{
    int value;
    char name[10];

} words;

words * string_mas;

string_mas = (words*) malloc(sizeof(words));
string_mas[0] = 5;

Хочу расширить еще на один элемент

string_mas =  (words*) realloc(string_mas, sizeof(words)*(num_str+1));

string_mas[1] = 8;
Но не работает, теряет предыдущие записи.

user2132
() автор топика
Ответ на: комментарий от user2132

Не должно оно ничего терять. realloc ничего не убирает. Видимо у тебя там какой-то баг в другом месте.

Кстати, советую результат функций malloc realloc calloc проверять на NULL.

SZT ★★★★★
()
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int compar(const void *a, const void *b)
{
	char * const *a_str = a;
	char * const *b_str = b;

	int a_int, b_int;

	if (sscanf(*a_str, "%*d:%*d:%*d %*s %*s %d %*s", &a_int) != 1) {
		fprintf(stderr, "cannot scan \"%s\"\n", a_str);
		exit(1);
	}

	if (sscanf(*b_str, "%*d:%*d:%*d %*s %*s %d %*s", &b_int) != 1) {
		fprintf(stderr, "cannot scan \"%s\"\n", b_str);
		exit(1);
	}

	return a_int - b_int;
}


int main(int argc, char **argv)
{
	if (argc < 1) {
		fprintf(stderr, "Usage: %s filename\n", argv[0]);
		return 1;
	}

	FILE *f = fopen(argv[1], "r");
	if (f == NULL) {
		fprintf(stderr, "Cannot open file \"%s\"\n", argv[1]);
		return 1;
	}

	char *buf = NULL;
	ssize_t buf_size = 0;

	size_t nlines = 0;
	size_t lines_length = 16;
	char **lines = malloc(sizeof(char *) * lines_length);
	if (lines == NULL) {
		perror("malloc");
		return 1;
	}

	for(;;) {
		if (getline(&buf, &buf_size, f) == -1) {
			if (feof(f)) {
				break;
			} else {
				perror("getline");
				return 1;
			}
		}
		
		if (nlines == lines_length) {
			lines_length *= 2;
			lines = realloc(lines, sizeof(char *) * lines_length);
			if (lines == NULL) {
				perror("realloc");
				return 1;
			}
		}

		lines[nlines] = strdup(buf);
		if (lines[nlines] == NULL) {
			perror("strdup");
			return 1;
		}
		++nlines;
	}

	qsort(lines, nlines, sizeof(char *), compar);

	for (size_t i = 0; i < nlines; ++i) {
		printf("%s", lines[i]);
		free(lines[i]);
	}
	free(lines);
	free(buf);
	fclose(f);
	return 0;
}

anonymous
()
Ответ на: комментарий от SZT

Спасибо за пример. А по поводу сортировки там ручками что-то типа пузырька делать ? Строк около 30-40 будет. Встроенного там нету ничего ведь ?

user2132
() автор топика
Ответ на: комментарий от SZT

А почему 3 ? Я обычно набирал типа man qsort, что там эта 3 значит, попробовал и так и с 3 вроде одно и тоже.

user2132
() автор топика
Ответ на: комментарий от user2132

Есть разные секции. Вот например если ты наберешь man printf то тебе дадут ман не по сишной функции printf, а по команде printf из GNU Coreutils которая будет из секции 1. А чтоб про сишный printf почитать, надо man 3 printf. Про эти секции можно почитать в man 7 man-pages

SZT ★★★★★
()
Ответ на: комментарий от anonymous

Можно смело «проверять» его assert().

А ещё можно его смело не проверять и прыгать с десятого этажа без парашюта.

Но не нужно.

anonymous
()
#include <assert.h>
#include <ctype.h>
#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>

static char **split(const char *in, size_t *nparts) {
  const char *p = in;
  size_t cnt = 0;
  while (*p) {
    while (*p && isspace(*p))
      p++;
    if (!*p)
      break;
    while (*p && !isspace(*p))
      p++;
    cnt++;
  }

  char **res = malloc(sizeof(char *) * (cnt + 1));
  assert(res);

  cnt = 0;
  p = in;
  while (*p) {
    while (*p && isspace(*p))
      p++;
    if (!*p)
      break;
    const char *start = p;
    while (*p && !isspace(*p))
      p++;
    res[cnt] = strndup(start, p - start);
    cnt++;
  }

  res[cnt] = NULL;
  if (nparts)
    *nparts = cnt;
  return res;
}

static void freev(char **s) {
  char **p = s;
  while (*p) {
    free(*p);
    p++;
  }
  free(s);
}

struct entry {
  char **parts;
  size_t nparts;
  char *line;
};

static int compare_entries(const void *a, const void *b) {
  const struct entry *entry_a = a;
  const struct entry *entry_b = b;

  return strcmp(entry_a->nparts > 3 ? entry_a->parts[3] : "",  //
                entry_b->nparts > 3 ? entry_b->parts[3] : ""); //
}

int main(void) {
  FILE *fp = fopen("data.txt", "rb");
  assert(fp);

  struct entry *entries = NULL;
  size_t entry_count = 0;

  char *line_buf = NULL;
  size_t line_buf_size = 0;
  while (getline(&line_buf, &line_buf_size, fp) != -1) {
    size_t line_len = strlen(line_buf);
    while (line_len > 0 && line_buf[line_len - 1] == '\n') {
      line_buf[line_len - 1] = 0;
      line_len -= 1;
    }

    entries = realloc(entries, sizeof(*entries) * (entry_count + 1));
    assert(entries);

    struct entry *entry = &entries[entry_count];
    entry_count += 1;

    entry->parts = split(line_buf, &entry->nparts);
    entry->line = strdup(line_buf);
    assert(entry->line);
  }
  free(line_buf);
  fclose(fp);

  for (size_t k = 0; k < entry_count; k++) {
    printf("original lines: %s\n", entries[k].line);
  }

  qsort(entries, entry_count, sizeof(*entries), compare_entries);

  for (size_t k = 0; k < entry_count; k++) {
    printf("sorted lines: %s\n", entries[k].line);
    free(entries[k].line);
    freev(entries[k].parts);
  }

  free(entries);
  return 0;
}
i-rinat ★★★★★
()
Ответ на: комментарий от ox55ff

Если на системе с overcommit кончилась память, то точно нужно прыгать. Т.к. уже ничего не спасёт.

С overcommit, без overcommit, если кончилась память, то надо точно-точно что-то делать. А assert опционален и может быть выпилен флагом. Поэтому нефиг.

anonymous
()
Ответ на: комментарий от anonymous

что-то делат

Нет, «что-то» точно не надо делать. Чтобы не создавать видимость наличия обработки ошибки там, где ты обработку этой ошибки не реализовал на самом деле, вводя людей в заблуждение.

anonymous
()
Ответ на: комментарий от pon4ik

Если у ОПа очень большой файл или ограниченное в ресурсах устройство, то mmap тогда не подойдёт

anonymous
()
Ответ на: комментарий от anonymous

если кончилась память, то надо точно-точно что-то делать

Завершать работу программы надо тогда.

И проверять значения *alloc надо не везде подряд, а там где надо. Например в инициализаторе класса проверять не нужно, а вот в коде из которого он будет вызываться, скорее всего нужно, и то зависит от логики.

RisuX3
()
Ответ на: комментарий от i-rinat

Из когда в код кочует этот split, хотя чуть поднапрячься и получится хорошо. При двух одинаковых циклов напрашивается просто один вложенный, assert внутри функции — негоже, в freev надо бы проверить перед разыменованием на NULL в начале. И вообще в 90% случаях далее результат split не будет меняться с новой аллокацией памяти, потому можно написать универсальную и быструю при этом варианте функцию.

На вид покажется, что оно разрослось, но это кажется, так как после препроцессора получится код короткий.

/* Установите в 1, если в последствии результат split()
   будет меняться с выделением новой памяти */
#define SPLIT_REZ_MEMCHG 0

#if SPLIT_REZ_MEMCHG
static void freev(char **s)
{
  if (s) {
	char **p = s;
	while (*p) free(*p++);
	free(s);
  }
}
#else
#define freev(s) free(s)
#endif

static char **split(const char *in, size_t *nparts)
{
  char **res = NULL, *m = NULL;
  size_t cnt = 0;
  const char *p = in;

  while(p) {
    while (1) {
	const char *start;

	while (*p && isspace(*p))
		p++;
	if (!*p)
		break;
	start = p;
	while (*p && !isspace(*p))
		p++;
	if(res) {
#if SPLIT_REZ_MEMCHG
		size_t msz = (p - in) - (start - in);
		m = malloc(msz + 1);
		if (m) {
			memcpy(m, start, msz);
			m[msz] = '\0';
			res[cnt] = m;
		} else {
			m = (void *)res;
			break;
		}
#else
		m[(p - in)] = '\0';
		res[cnt] = m + (start - in);
#endif
	}
	cnt++;
    }
    if (res) {
	res[cnt] = NULL;
#if SPLIT_REZ_MEMCHG
	if (m == (void *)res) {
		freev(res);
		cnt = 0;
		res = NULL;
		break;
	}
#endif
	break;
    }
    cnt = sizeof(char *) * (cnt + 1);

#if SPLIT_REZ_MEMCHG
    res = malloc(cnt);
#else
    res = malloc(cnt + (p - in) + 1);
#endif

    if (res == NULL) {
	cnt = 0;
	break;
    }

#if !SPLIT_REZ_MEMCHG
    m = (void *)res + cnt;
    memcpy (m, in, (p - in) + 1);
#endif

    p = in;
    cnt = 0;
  }

  if (nparts)
    *nparts = cnt;
  return res;
}

vodz ★★★★★
()
Ответ на: комментарий от i-rinat
while (line_len > 0 && line_buf[line_len - 1] == '\n') {
      line_buf[line_len - 1] = 0;
      line_len -= 1;
    }

Зачем тут while? Тут и if достаточно, без line_len--;

И для этого split удалять '\n' не надо.

И вообще, зачем вам в этом коде split вообще? Просто чтоб было? ;)

vodz ★★★★★
()
Ответ на: комментарий от vodz

Код нужно писать не выпендриваясь, по возможности. Так потом легче жить. Минимизируется время на чтение и понимание.

зачем вам в этом коде split вообще

Код пишется для людей, а не для машин. Машине и машинные коды хорошо заходят.

i-rinat ★★★★★
()
Последнее исправление: i-rinat (всего исправлений: 1)
Ответ на: комментарий от ox55ff

Вроде там был какой-то педон для микроконтроллеров.

anonymous
()

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

простой скрипт

anto215 ★★
()
Ответ на: комментарий от i-rinat

Код нужно писать не выпендриваясь, по возможности. Так потом легче жить. Минимизируется время на чтение и понимание.

Ну понятное дело, что для некоторых лаконичность и использование полных возможностей указателей переходит в уровень обфускации. Но у меня прям всё вскипело, когда я увидел опять эту split с двумя одинаковыми циклами. Я же мог и по другим поводам придраться. Например, getline+strlen — это же лишний повод отправить в man по getline.

Код пишется для людей, а не для машин. Машине и машинные коды хорошо заходят.

Вы повторяетесь, но на вопрос так и не ответили — в вашем коде результат split не юзается.

vodz ★★★★★
()
Последнее исправление: vodz (всего исправлений: 1)
Ответ на: комментарий от vodz

Но у меня прям всё вскипело, когда я увидел опять эту split с двумя одинаковыми циклами.

И ты мигом «оптимизировал» его в непонятное месиво. Предыдущий код можно было проверить просто прочитав, в новом нужно разбираться.

Например, getline+strlen — это же лишний повод отправить в man по getline.

Не люблю присваивания в условиях циклов. Но да, как-то криво в ман посмотрел.

КАКОЙ КОШМАР!!! Лишний вызов strlen!!! Это же горячий код, который будет вызван МИЛЛИАРДЫ раз каждый день!

А если серьёзно, то ты уже потратил на обсуждение больше времени, чем отказ от strlen сэкономит в этой программе за всё время её существования. Так что оптимизация ради оптимизации это прикольно, но непосредственной пользы не несёт.

результат split не юзается

Правда, что ли? Ну занули parts тогда, проверь результаты.

i-rinat ★★★★★
()
Ответ на: комментарий от vodz

Из когда в код кочует этот split

Я с потолка его взял, так что конкретно его кочевой путь начался вчера. Если есть возможность, я бы заюзал glib. Там уже есть много всего полезного, не нужно велосипеды изобретать.

i-rinat ★★★★★
()
Последнее исправление: i-rinat (всего исправлений: 1)
Ответ на: комментарий от i-rinat

Но да, как-то криво в ман посмотрел.

На всякий случай, из того же man-а, если всегда вызывать с getline(NULL...), то и дальнейший stdup не нужен, не то что это большая разница, но код красивее.

Правда, что ли?

А, ну да. Я не в вчитывался в оригинальное ТЗ, так как вопрос не интересный, потому без коментариев, что это делает, мельком и не видно, что строки то оригинальные, но отсортированы по их части.

Так что оптимизация ради оптимизации

Ни в коем разе. Оптимизации в режиме «допустимо изменение» вообще нет, так как в каждом цикле происходит if(res), что замедляет их оба. Оптимизация там именно для просмотра и использования. Это же два кода, надо их писать раздельно с пояснениями, какой для чего. Стоит их так написать, как получится лакончно, обозримо, и, создаст батхёрт только у указателененавистников.

vodz ★★★★★
()
Ответ на: комментарий от i-rinat

Там что, нужны комментарии?

Не читая ТСа - да. Перед sort написать - сортируем по 4-й части. Иначе при беглом взгляде если пропустить пристальное рассматривание compare, создалось ощущение, что тупо сотируем строки и зачем split - непонятно.

vodz ★★★★★
()
Ответ на: комментарий от vodz

если пропустить пристальное рассматривание compare, создалось ощущение, что тупо сотируем строки

Ну значит, я налажал с читаемостью. Правда, я не представляю, как сделать компаратор ещё проще и очевиднее.

Перед sort написать

Такие комментарии постоянно врут. Компаратор меняют, а про комментарий забывают.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Правда, я не представляю, как сделать компаратор ещё проще и очевиднее.

Да элементарно, самодокументируемостью. Тогда и забытые неправильные комментарии не нужны. Обозвали бы как compare_4part и всё.

vodz ★★★★★
()
Ответ на: комментарий от vodz

самодокументируемостью

Меня всё удивляет, как предложенные правки к split(), превращающие код месиво — нормально, а вот четыре строчки компаратора, из которых две это вообще преобразования типов, непонятны.

compare_4part

Такие названия не помогают, а только больше запутывают. Смотрю на подобные имена, и начинаю думать: «хорошо, вот этот вариант расшифровки названия ложится на моё предположение. Это хорошо, но могут ли тут быть ещё и другие толкования?» И другие толкования придумываются. Далеко не факт, что под 4part означает 4th part. Это может быть for part. 4 может означать пятый элемент, ведь «настоящие сишники считают с 0». И тому подобное.

i-rinat ★★★★★
()
Ответ на: комментарий от anonymous

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

Если кто-то до сих пор, после всех найденных в сишных программах уязвимостей, доверяет видимости обработки ошибки, то его надо отправлять писать на жабоскрипте.

anonymous
()
Ответ на: комментарий от RisuX3

Завершать работу программы надо тогда.

Неужели? А чего бы не подождать в цикле, пока память не появится?

Например в инициализаторе класса проверять не нужно, а вот в коде из которого он будет вызываться, скорее всего нужно, и то зависит от логики.

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

anonymous
()
Ответ на: комментарий от i-rinat

Меня всё удивляет, как предложенные правки к split(), превращающие код месиво — нормально,

Нормальненько так. То есть объяснения, что это враньё — тихо скипнули, и всё долбим?

а вот четыре строчки компаратора, из которых две это вообще преобразования типов, непонятны.

И опять враньё. С самого начала было сказано, что код просматривался бегло, потом три раза было объяснено, что вопрос был по причине непросмотра compare у и далее по списку.

Всегда в вас удивляло, что разные поправки к вашему коду, либо молча пропустили как с проверкой разыменований, либо раздули с криками (ПАДУМАЕШЬ). Зато уже десятый комментарий долбите одно и тоже, о котором давно уже всё обсосано почему и так получилось. Неужели непонятно, что это отвратительно?

Смотрю на подобные имена, и начинаю думать:

В этом вся и фишечка, смотрите и думаете. А когда compare_entry, где entry — вся строка и смотреть не всегда интересно.

vodz ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.