polskie znaki w pliku CSV

0

Witam,

Przeszukałem przez ostatni tydzień trochę internetu i mam niezłą zagwozdkę.

Generalnie użytkownicy mają mieć możliwość wprowadzania danych do systemu za pomocą pliku .CSV. problem polega na tym, że jak w programie Excel wpisuje polskie znaki to po zapisaniu w formacie CSV, zamknięciu i ponownym otworzeniu w Excel'u lub notatniki, Notepad++ czy czymkolwiek innym, zamiast polskich znaków pojawiają się krzaki. Okazuje się, że plik zapisywany jest w ANSI i traci bezpowrotnie informacje o polskich znakach... Nawet jak w Notepad++ zmienie kodowania na UTF8 to nic nie pomaga. Chcę, żeby plik był zapisany w UTF8, z Polskimi znakami, gdyż bazę PostgreSQL mam i chcę mieć w UTF8.

Myślałem, że to wina mojego komputera, gdyż mam Windows po angielsku, a z kolei u koleżanki, która ma po polsku widać polskie znaki w CSV. U niej normalnie widać, a po przesłaniu tego samego pliku mailem do mnie i otwarciu u mnie na komputerze, od razu są krzaki.

Nie przejmowałbym się problemem, gdyby importowanie pliku do bazy poprzez PHP działało poprawnie i polskie znaki się zapisywały w bazie, tak się jednak nie dzieje... Poniżej kod PHP, który mam (używam nawet funkcji do zmiany kodowania Stringa na UTF8).

$uploaddir = '/home/.../dev/system/admin/temp/';
                    $uploadfile = $uploaddir . basename($_FILES['userfile']['name']);


                    if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
                        $filename=$uploadfile;
                        $handle = fopen($filename, "r");
                        if ($handle) {
                            $liczba=0;
                            while (($line = fgets($handle)) != false) {
                                $line=utf8_encode($line);
                                // tutaj wycągam poprzez StringTokenizer dane z linii z CSV
                                $query="insert into users(email, password, name, adres, poczta, telefon) values('".$email."', '".md5('zaq12WSX')."', '".$name."', '".$adres."', '".$poczta."', '".$telefon."');";
                                $result=pg_query($db,$query);
                            }
 

O co proszę:

  1. O pomoc z tymi plikami CSV bo chyba nie da mi to spokoju, jak zapisać plik CSV w UTF8, aby był rozdzielony średnikami i aby miał polskie znaki
  2. Generalnie sposób na Upload pliku CSV na serwer i poprawne przerzucenie stringów z polskimi znakami do bazy

Z góry serdecznie dziękuję za wszelką pomoc!

Pozdr,
drejas

0

Excel ma bardzo podstawową i niewygodną obsługę CSV. Testowałem pod tym względem oprócz Excela odpowiedniki z pakietów: Open Office, Kingsoft Office, Libre Office - i jeżeli dobrze pamiętam to ten ostatni był najlepszy - przy otwieraniu wybierasz kodowanie, czym rozdzielać kolumny i masę innych rzeczy.

Edit: Boooooooooże, używasz Postgresa, a dziury robisz tak beznadziejne jak newbie z MySQL. SQL Injection, poważna dziura!

Spróbuj z iconv, ale generalnie możesz mieć problem, bo jak zauważyłeś - różni ludzie mogą podsyłać pliki w różnym kodowaniu. Niektóre kodowania są nie do rozróżnienia, więc iconv nie zawsze zadziała.

A do CSV masz fgetcsv, a nie jakiś tokenizer, który może nawalić przy skomplikowanych danych.

0

Dzięki dzek69. O SQL Injection akurat tutaj się nie boję, nie wklejam przecież całości kodu, nie wiesz też kto ma tego używać, dosłownie 3 osoby i jest moim zdaniem dobrze zabezpieczone przed tym, żeby nikt inny nigdy nie zobaczył tej strony gdzie może zrobic upload pliku .csv. Tak czy inaczej na razie skupiam się na parsowaniu tego pliku, żeby były jednak te polskie znaki niezależnie od tego kto co podeśle..

Dzięki za podpowiedź, który pakiet biurowy dobrze by to zakodował, jednak nie mogę powiedzieć użytkownikom, że mają to zainstalować i zrobić im instrukcji jak to kodować. Może sprawdzić by u nich na kompach jak są zakodowane te pliki i rzeczywiście zrobić iconv?

Dzięki też za tip z fgetcsv. Jakoś nie miałem chęci do użycia tego, ale skoro polecasz to spróbuję z tym.

Jak ktoś ma jakieś pomysły lub doświadczenia z tym to dalej czekam na pomoc i z góry dziękuję :)

Pozdr,
drejas.

1

Robienie dziur, "bo to tylko dla mnie i nikt tego nie widzi" to po prostu zła praktyka. Jakby kolega-budowlaniec odwalał szajs budując dom, "bo to przecież dla mnie" to też bym mu zwrócił uwagę, że to, co robi nie jest rozsądne.

iconv jak mu podasz pierwszy parametr jako pusty string to spróbuje wykryć kodowanie - i to jest najlepsze, co możesz zrobić.
fgetcsv odwala część roboty za Ciebie, w dodatku mając na uwadze wszystkie rzeczy, o których Ty czy nawet ja możemy nie mieć pojęcia. Nawiązując znowu do budownictwa - można dziurę w ziemii wykopać widłami i wspomagać się łyżeczką, ale lepiej wziąć łopatę.

Edit: Wrzucam kawałek kodu z projektu, w którym robiłem import CSV:

        if (file_exists($sCSVPath)) {
            if (($fp = fopen($sCSVPath, 'r')) !== false) {
                while (($data = fgetcsv($fp, 0, ";")) !== FALSE)  {
                    $aOCFixData[(int) $data[0]] = [
                        'customer_raw'=>iconv('', 'utf-8', $data[1]),
                        'date_from'=>dt::formatDate($data[2])." 10:00:00",
                        'date_to'=>dt::formatDate($data[3])." 09:50:00",
                        'car_raw'=>$data[4],
                    ];
                }
            }
        }
0

Oczywiście masz racje dzek69. Dzięki za pomoc, zaimplementuje to u siebie i dam znać wieczorem jaki wynik.

0

OK udało się rozwiązać problem. Wyciagam całą zawartość pliku, zmieniam kodowanie i ładuje ją z powrotem. Poniżej kod:

$uploaddir = '/home/dev/system/admin/temp/';
                    $uploadfile = $uploaddir . basename($_FILES['userfile']['name']);


                    if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
                        $filename=$uploadfile;
                        rcp($filename);
                        if (file_exists($filename)) {
                            if (($fp = fopen($filename, 'r')) !== false) {
                                while (($data = fgetcsv($fp, 0, ";")) !== FALSE)  {
                                    echo $data[0].'<br>';
                                    echo $data[1].'<br>';
                                    echo $data[2].'<br>';
                                    echo $data[3].'<br>';
                                    echo $data[4].'<br>';
                                }
                            }
                        }
                    } else {
                        echo '<span class="normal" align="left">Wystąpił błąd przy wysyłaniu pliku!</br></br></span>';
                    }

function rcp($plik){
		$buf="";

		$pl=fopen($plik, "r");
		if(!$pl) return;
		while(!feof($pl)) $buf.=fread($pl, 100000);
		fclose($pl);
		
		$buf=iconv("WINDOWS-1250", "UTF-8", $buf);

		$pl=fopen($plik, "w");
		if(!$pl) return;
		fwrite($pl, $buf);
		fclose($pl);
	}
 

dzieki dziek69 za pomoc!

0

Tylko tu na sztywno zakładasz, że kodowanie to Windows-1250. Autodetekcja nie zadziałała?

0

właśnie nie... warto by to dopracować masz racje, próbowałem zostawić puste jak mówiłeś, próbowałem też tak:
$buf=iconv(mb_detect_encoding($buf), "UTF-8", $buf);

Jednak z niektórymi plikami to działa, z niektórymi nie.. nie wiem dlaczego nie dla 100% plików.

Pozdr,
drejas

1 użytkowników online, w tym zalogowanych: 0, gości: 1