[C] Obsługa pliku .txt. Wczytywanie całego słowa.

SetoKami
Użytkownik
Użytkownik
Posty: 56
Rejestracja: 16 paź 2010, o 10:36
Płeć: Mężczyzna
Lokalizacja: Poland
Podziękował: 9 razy

[C] Obsługa pliku .txt. Wczytywanie całego słowa.

Post autor: SetoKami »

Witam
Chciałem napisać program w języku C, ponieważ w nim się mniej więcej trochę orientuję.
Ale do rzeczy. Mam plik txt w którym jest dużo wersów z różnymi słowami, cyframi itd. Potrzebuję z tego pliku wyłapać wszystkie (w tym wypadku) nicki i zapisać je jeden pod drugim w osobnym pliku txt. Zauważyłem, że nicki są podane w wersach zaczynających się od słowa "Wyświetlenia:" następnie jest liczba (może być dwucyfrowa, trzycyfrowa itd.), a po tych dwóch obiektach jest wyraz, który jest nickiem. Niżej pokażę kawałek takiego pliku:

Kod: Zaznacz cały

Miniatura11:38Dodaj do
Let's play Pokemon Fire Red [PL]...
Let's play Pokemon Fire Red [PL][HD] #005
Pokemon Fire red PL do sciagnięcia: speedyshare.com/files/29065080/Poke...
Wyświetlenia: 50 GBARomPL
Miniatura8:33Dodaj do
Pierwszy raz ... z MMO Champions...
Pierwszy raz ... z MMO Champions Online Free for All p.1
Niech mi youtube wreszcie zdejmie ten limit bo się potne.
Wyświetlenia: 92 L4GTV
Miniatura2:19Dodaj do
Power Lies In GMV!
Power Lies In GMV!
Old collab. Ive finally decided to post it =0
Wyświetlenia: 181 KatsuhitoTheEmperor
Miniatura3:19Dodaj do
Minecraft na Wybuchowo odc. 4
Minecraft na Wybuchowo odc. 4
Szukałem czy jest jakaś funkcja wczytująca całe wyrazy ale nie znalazłem nic pod programowanie w C. Jeżeli jest taka funkcja to bardzo proszę o podanie jak działa i do jakiego typu zmiennej taki wyraz wczytać. I jak zapisywać taki wyraz do nowego pliku? No i jak zrobić przejście do nowej linii w pliku wyjściowym, żeby wyrazy były pod sobą?
Jeżeli ktoś ma łatwiejszy sposób na zrobienie takiego programu to chętnie posłucham.
Z góry bardzo dziękuję i pozdrawiam!
void_t
Użytkownik
Użytkownik
Posty: 103
Rejestracja: 14 maja 2011, o 18:25
Płeć: Mężczyzna
Pomógł: 26 razy

[C] Obsługa pliku .txt. Wczytywanie całego słowa.

Post autor: void_t »

Hm, właściwie to żadnej funkcji, która zrobiłaby to za Ciebie nie ma. Możesz się ewentualnie zainteresować PCRE w C, ale to wymaga większego nakładu pracy. Oczywiście da sie to zrobić i bez wyrażeń regularnych. Musisz wczytywać z pliku bajt po bajcie i sprawdzać czy sekwencja Ci odpowiada, proste.

Abstrahując jednak od C, w perlu rozwiązanie tego jest zaledwie na kilka linijek:

Kod: Zaznacz cały

#!/usr/bin/perl

$fp = undef;
open($fp, "<", "input.txt");

my @result;
while (<$fp>) {
	if (my @nick = $_ =~ m/^Wyświetlenia:(?:s+)?d+(?:s+)(w+)/gx) {
		push(@result, $nick[$#nick]);
	}
	
}
print @result;
Gdybyś oczywiście chciał to jednak napisać w C, mów, a pomoc przyjdzie
SetoKami
Użytkownik
Użytkownik
Posty: 56
Rejestracja: 16 paź 2010, o 10:36
Płeć: Mężczyzna
Lokalizacja: Poland
Podziękował: 9 razy

[C] Obsługa pliku .txt. Wczytywanie całego słowa.

Post autor: SetoKami »

No niestety o perl'u nawet nie słyszałem. Ja zawsze robiłem programy mało zoptymalizowane i naprawdę proste. To jest trochę bardziej złożone. Taki program potrzebny mi jest tylko, żeby ułatwić pracę i nie bawić się tym z "palca" usuwając niepotrzebne linijki w txt. Stwierdziłem, że to co robię ja, może zrobić program tak samo myśląc :)
void_t
Użytkownik
Użytkownik
Posty: 103
Rejestracja: 14 maja 2011, o 18:25
Płeć: Mężczyzna
Pomógł: 26 razy

[C] Obsługa pliku .txt. Wczytywanie całego słowa.

Post autor: void_t »

Kod: Zaznacz cały

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

// definicje znaków nowej linii 
#define LF (char) 10 
#define CR (char) 13	

void usage(char *name);

typedef struct nick_node_t {
	char			*nick;
	struct nick_node_t	*next;
	struct nick_node_t	*prev;
} nick_node_t;

typedef struct nick_list_t {
	nick_node_t	data;
	nick_node_t	*last;
} nick_list_t;

nick_list_t *parse_buffer(char *buf)
{
	char *p;
	char *b;
	char nick[256]; // ustalmy taki limit, nikt chyba i tak nie ma
			// 256 znakowego nicku
        unsigned int keyl;
	nick_list_t *nlist;
	
	// ok, wiemy, że rzeczy które nas interesują zawsze
	// są zlokalizowane po słowie kluczu "Wyświetlenia:".
	// zatem wykorzystajmy to.
#define KEY "Wyświetlenia:"	
	keyl = strlen(KEY);
#undef KEY

	// alokacja pamięci dla listy
	nlist = calloc(1, sizeof(nick_list_t));
	if (!nlist) return NULL;
	nlist->last = &nlist->data;

	for (p = buf; *p != '\0'; p++) {
		// troche zoptymalizowana procedura wyłapująca
		// słowo klucz, fakt jest faktem, że jeśli
		// to będzie inne słowo - program pobierze nie nick
		// a coś innego, anyway, na nasze potrzeby chyba wystarczy,
		// ale ok: krótka adnotacja co się tutaj dzieje: otóż
		// UTF-8 nie jest domyślnie supportowany (a zakładam że plik
		// wejściowy jest w tym kodowany), wówczas przesunięcie pointera
		// o 1 w przypadku gdy mamy znaki UTF-8 spowoduje, że
		// dostaniemy się w literę, a nie tak jak chcieliśmy - poza nią.
		// stąd też użyłem (brzydko to wygląda) strlen
		if (*p == 'W' && *(p+1) == 'y' &&
		    *(p+strlen("ś")+2) == 'w') {
			// przesuwamy pointer do tego co nas interesuje.
			// będzie to o tyle sprytne, że wykorzystamy fakt
			// iż po "Wyświetlenia:" znajduje się spacja, jakaś liczba,
			// spacja, *nick*
			p = p+keyl + 2;
			
			// wyrównujemy pozycje do nicku
			for (;;p++)
				if (*p == ' ') { p++; break; }
			
			// to l jest niczym innym jak licznikiem
			// znaków nicku
			int l = 0;
			for (;;p++, l++) {
				if (*p == CR || *p == LF) { break;}
			}
			
                        // normalizujemy, na wypadek gdyby ktoś jednak miał bardzo długi nick
			if (l > 256) l = 0xFF;
			memset(nick, 0, 0xFF);
			memcpy(nick, p - l, l);
			nlist->last->nick = strndup(nick, l);
			nlist->last->next = calloc(1, sizeof(nick_node_t));
			if (!nlist->last->next) return NULL;
			nlist->last->next->prev = nlist->last;
			nlist->last = nlist->last->next;
		}

	}
	return nlist;
}

int main(int argc, char **argv)
{
	FILE		*fp;
	char		*buf;
	size_t		len;
	nick_list_t	*nicks;
	nick_node_t	*node;

	// sprawdzamy, czy podano wystarczającą ilość argumentów
	if (argc < 3)
		usage(argv[0]);
	
	// otwieramy plik wejściowy tylko do odczytu
	fp = fopen(argv[1], "r");
	// jeśli otwieranie się nie powiodło - boom, wychodzimy z programu
	if (!fp) goto err;
	// w przeciwnym razie, well, wczytamy wszystko do jakiegoś
	// bufora - trochę to nieoptymalne rozwiązanie
	// ale jego optymalizację pozostawiam korzystającemu ;>
	//
	// najpierw dowiemy się jak długi jest plik
	fseek(fp, 0, SEEK_END);
	len = ftell(fp);
	fseek(fp, 0, SEEK_SET);
	// alokujemy jeden duży blok pamięci
	// i sprawdzamy czy faktycznie system go nam przydzielił
	buf = calloc(1, len + 1);
	if (!buf) goto err;
	
	// przydzielił, więc wczytujemy wszystko do bufora
	if (!fread(buf, 1, len, fp)) goto err;

	// ok mamy wszystko, czas to przeparsować
	nicks = parse_buffer(buf);

        // jeśli nie mamy żadnych wyników, cóż - pozostaje nam wyjść
        if (!nicks) return 0;

	// wyświetlamy wszystkie nicki
	for (node = &nicks->data; node->next != NULL; node = node->next)
		puts(node->nick);

	// zwalniamy zasoby
	free(buf);
	fclose(fp);
	for (node = nicks->last; node != NULL; node = node->prev) {
		free(node->nick);
		free(node->next);
	}
	free(nicks);	
	return 0;
err:
	puts("Program exited due to error");
	return 0;
}


void usage(char *name)
{
	printf("Usage: %s [INPUT] [OUTPUT]\n", name);
	exit(0);
}
Troche mi się nudziło - przyznaję. Kod nie jest zoptymalizowany - proszę zatem nie krzyczeć.
Naturalnie dopisz sobie tą część, która do pliku wrzuci wyniki.
SetoKami
Użytkownik
Użytkownik
Posty: 56
Rejestracja: 16 paź 2010, o 10:36
Płeć: Mężczyzna
Lokalizacja: Poland
Podziękował: 9 razy

[C] Obsługa pliku .txt. Wczytywanie całego słowa.

Post autor: SetoKami »

Dziękuję serdecznie za takie chęci napisania tego wraz z komentarzem Bardzo dużo jest tutaj rzeczy, których nie pojmuję. Np. o strukturach tylko słyszałem, nigdy ich nie wykorzystywałem. Kompiluję to też Dev-C++ i mi wyskakuje błąd w 77 linii przy używaniu "strndup". Aha no i jak plik txt musi się nazywać jak go otwieramy? Bo ja używałem kiedyś otwierania takiego np:

Kod: Zaznacz cały

FILE *Plik_we;
Plik_we = fopen("c:\plik_we.txt", "r");
void_t
Użytkownik
Użytkownik
Posty: 103
Rejestracja: 14 maja 2011, o 18:25
Płeć: Mężczyzna
Pomógł: 26 razy

[C] Obsługa pliku .txt. Wczytywanie całego słowa.

Post autor: void_t »

Nie ma za co.

Kod: Zaznacz cały

fp = fopen(argv[1], "r");
Nie wiesz co to argv[1]? Well, funkcja main pełni rolę entry pointu aplikacji, stąd też do niej przekazuje się argumenty przesłane od użytkownika. int argc to skrót zapewne od argument's-counter - czyli poprostu licznik podanych argumentów. argv to tablica argumentów - argv[0] - tutaj zawsze znajduje się nazwa programu który wykonujemy (czyli np nasz program nazywa się parser.exe to zobaczymy tam właśnie parser.exe), argv[1] - pierwszy argument do programu itd.

Przykładowe użycie tego, znajduje się w funkcji usage()

Kod: Zaznacz cały

printf("Usage: %s [INPUT] [OUTPUT]\n", name);
Czyli w konsoli cmd.exe uruchomimy to:
parser.exe C:\plik_wejsciowy.txt C:\plik_wyjsciowy.txt
Ten rodzaj struktury jaki tutaj zastosowałem nazywa się listą podwójnie łączoną, koniecznie musisz sie z tego rodzaju algorytmami zapoznać - google.pl Ci w tym bez wątpienia pomoże ;-)
(btw, polecam czytać doskonały wręcz kod nginxa, celem obycia się z nowymi zagadnieniami - serwera www).

Teraz wracając do błędu o którym mowisz. Jaki jest komunikat? Bo deklaracja funkcji strndup zawarta jest w pliku nagłówkowym string.h, który oczywiście jest zaincludowany w kodzie.
SetoKami
Użytkownik
Użytkownik
Posty: 56
Rejestracja: 16 paź 2010, o 10:36
Płeć: Mężczyzna
Lokalizacja: Poland
Podziękował: 9 razy

[C] Obsługa pliku .txt. Wczytywanie całego słowa.

Post autor: SetoKami »

Błąd mam następujący (żeby być dokładnym):
void_t
Użytkownik
Użytkownik
Posty: 103
Rejestracja: 14 maja 2011, o 18:25
Płeć: Mężczyzna
Pomógł: 26 razy

[C] Obsługa pliku .txt. Wczytywanie całego słowa.

Post autor: void_t »

Mhm, no faktycznie, wygląda na to, że funkcja nie istnieje.
Zatem zamiast:

Kod: Zaznacz cały

nlist->last->nick = strndup(nick, l);
Wstaw

Kod: Zaznacz cały

nlist->last->nick = strdup(nick);
SetoKami
Użytkownik
Użytkownik
Posty: 56
Rejestracja: 16 paź 2010, o 10:36
Płeć: Mężczyzna
Lokalizacja: Poland
Podziękował: 9 razy

[C] Obsługa pliku .txt. Wczytywanie całego słowa.

Post autor: SetoKami »

Fantastycznie! Dziękuję! Wszystko działa tak jak chciałem. Udało mi się wczytać w końcu te pliki wejściowe txt i wyjściowe. Ładnie się wyświetlają nicki w konsoli Ale mogę mieć jeszcze jedno pytanie. Jak teraz zapisywać do tego otwartego pliku wyjściowego (plik_wyjsciowy.txt)?
void_t
Użytkownik
Użytkownik
Posty: 103
Rejestracja: 14 maja 2011, o 18:25
Płeć: Mężczyzna
Pomógł: 26 razy

[C] Obsługa pliku .txt. Wczytywanie całego słowa.

Post autor: void_t »

Otwartego? Hm. W kodzie otwarty jest jedynie plik_wejściowy.txt.
Do pliku wyjściowego - jak do normalnego pliku, tyle, że musisz go otworzyć do zapisu (jego nazwa jest w argv[2]), po czym pokombinować w instrukcji

Kod: Zaznacz cały

 // wyświetlamy wszystkie nicki
   for (node = &nicks->data; node->next != NULL; node = node->next)
      puts(node->nick);
by zastąpić puts, fwrite.
SetoKami
Użytkownik
Użytkownik
Posty: 56
Rejestracja: 16 paź 2010, o 10:36
Płeć: Mężczyzna
Lokalizacja: Poland
Podziękował: 9 razy

[C] Obsługa pliku .txt. Wczytywanie całego słowa.

Post autor: SetoKami »

No wiem, że mam zamienić puts na fwrite tam i otworzyć. Zrowbiłem to następująco:

Kod: Zaznacz cały

// otwieramy plik wyjściowy do zapisu     
   fp = fopen(argv[2], "w");
   
   // wyświetlamy wszystkie nicki
   for (node = &nicks->data; node->next != NULL; node = node->next)
      {
      fwrite(&nicks, sizeof(nicks), 1, fp);
      puts(node->nick);
      }
Ale niestety takie dziwne krzaczki mi się zapisują w pliku wyjściowym.
void_t
Użytkownik
Użytkownik
Posty: 103
Rejestracja: 14 maja 2011, o 18:25
Płeć: Mężczyzna
Pomógł: 26 razy

[C] Obsługa pliku .txt. Wczytywanie całego słowa.

Post autor: void_t »

Well, robisz to źle.

Kod: Zaznacz cały

fwrite(&nicks, sizeof(nicks), 1, fp);
&nicks to pointer na liste, a nie na węzeł w którym to umiejscowiony jest nick.

Kod: Zaznacz cały

fwrite(node->nick, sizeof(char), strlen(node->nick), fp);
SetoKami
Użytkownik
Użytkownik
Posty: 56
Rejestracja: 16 paź 2010, o 10:36
Płeć: Mężczyzna
Lokalizacja: Poland
Podziękował: 9 razy

[C] Obsługa pliku .txt. Wczytywanie całego słowa.

Post autor: SetoKami »

Aj, aj też tak próbowałem ale zapomniałem o "strlen(node->nick)"
A czy ustawianie pointera w nowym wierszu zrobić z wykorzystaniem funkcji "fseek" czy jest jakiś szybszy sposób? Bo w pliku wyjściowym stringi są wrzucane jeden za drugim.
void_t
Użytkownik
Użytkownik
Posty: 103
Rejestracja: 14 maja 2011, o 18:25
Płeć: Mężczyzna
Pomógł: 26 razy

[C] Obsługa pliku .txt. Wczytywanie całego słowa.

Post autor: void_t »

Możesz zrobić drugie wywołanie fwrite, które zapisze znak nowej linii - '\n'.
SetoKami
Użytkownik
Użytkownik
Posty: 56
Rejestracja: 16 paź 2010, o 10:36
Płeć: Mężczyzna
Lokalizacja: Poland
Podziękował: 9 razy

[C] Obsługa pliku .txt. Wczytywanie całego słowa.

Post autor: SetoKami »

A czy "
" jest typu char? Chodzi mi tu o podawany rozmiar z funkcji fwrite

Kod: Zaznacz cały

for (node = &nicks->data; node->next != NULL; node = node->next)
      {
      fwrite(node->nick, sizeof(char), strlen(node->nick), fp);
      fwrite("
",sizeof(char),1,fp);
      puts(node->nick);
      }
ODPOWIEDZ