web-dev-qa-db-fra.com

fputcsv et codes de nouvelle ligne

J'utilise fputcsv dans PHP pour sortir un fichier délimité par des virgules d'une requête de base de données. Lors de l'ouverture du fichier dans gedit dans Ubuntu, il semble correct - chaque enregistrement a un saut de ligne (pas visible les caractères de saut de ligne, mais vous pouvez dire que chaque enregistrement est séparé, et son ouverture dans la feuille de calcul OpenOffice me permet de visualiser le fichier correctement.)

Cependant, nous envoyons ces fichiers à un client sous Windows, et sur leurs systèmes, le fichier se présente sous la forme d'une longue et longue file d'attente. En l'ouvrant dans Excel, il ne reconnaît pas du tout plusieurs lignes.

J'ai lu plusieurs questions ici qui sont assez similaires, y compris celle-ci , qui comprend un lien vers l'explication vraiment informative Great Newline Schism .

Malheureusement, nous ne pouvons pas simplement dire à nos clients d'ouvrir les fichiers dans un éditeur "plus intelligent". Ils doivent pouvoir les ouvrir dans Excel. Existe-t-il un moyen par programme de s'assurer que les caractères de nouvelle ligne appropriés sont ajoutés afin que le fichier puisse être ouvert dans un tableur sur n'importe quel système d'exploitation?

J'utilise déjà une fonction personnalisée pour forcer les guillemets autour de toutes les valeurs, car fputcsv est sélectif à ce sujet. J'ai essayé de faire quelque chose comme ça:

function my_fputcsv($handle, $fieldsarray, $delimiter = "~", $Enclosure ='"'){

        $glue = $Enclosure . $delimiter . $Enclosure;

    return fwrite($handle, $Enclosure . implode($glue,$fieldsarray) . $Enclosure."\r\n");

}

Mais lorsque le fichier est ouvert dans un éditeur de texte Windows, il apparaît toujours comme une seule longue ligne.

29
EmmyS
// Writes an array to an open CSV file with a custom end of line.
//
// $fp: a seekable file pointer. Most file pointers are seekable, 
//   but some are not. example: fopen('php://output', 'w') is not seekable.
// $eol: probably one of "\r\n", "\n", or for super old macs: "\r"
function fputcsv_eol($fp, $array, $eol) {
  fputcsv($fp, $array);
  if("\n" != $eol && 0 === fseek($fp, -1, SEEK_CUR)) {
    fwrite($fp, $eol);
  }
}
35
John Douthat

Il s'agit d'une version améliorée de l'excellente réponse de @John Douthat, préservant la possibilité d'utiliser des délimiteurs et des boîtiers personnalisés et de renvoyer la sortie d'origine de fputcsv:

function fputcsv_eol($handle, $array, $delimiter = ',', $Enclosure = '"', $eol = "\n") {
    $return = fputcsv($handle, $array, $delimiter, $Enclosure);
    if($return !== FALSE && "\n" != $eol && 0 === fseek($handle, -1, SEEK_CUR)) {
        fwrite($handle, $eol);
    }
    return $return;
}
23
lucaferrario

L'utilisation de la fonction php fputcsv écrit uniquement\n et ne peut pas être personnalisée. Cela rend la fonction sans valeur pour l'environnement Microsoft, bien que certains packages détectent également la nouvelle ligne Linux.

Pourtant, les avantages de fputcsv m'ont permis de chercher une solution pour remplacer le caractère de nouvelle ligne juste avant de l'envoyer dans le fichier. Cela peut être fait en diffusant d'abord le fputcsv vers le build dans le flux temp php. Ensuite, adaptez le ou les caractères de nouvelle ligne à ce que vous voulez, puis enregistrez-les dans un fichier. Comme ça:

function getcsvline($list,  $seperator, $Enclosure, $newline = "" ){
    $fp = fopen('php://temp', 'r+'); 

    fputcsv($fp, $list, $seperator, $Enclosure );
    rewind($fp);

    $line = fgets($fp);
    if( $newline and $newline != "\n" ) {
      if( $line[strlen($line)-2] != "\r" and $line[strlen($line)-1] == "\n") {
        $line = substr_replace($line,"",-1) . $newline;
      } else {
        // return the line as is (literal string)
        //die( 'original csv line is already \r\n style' );
      }
    }

        return $line;
}

/* to call the function with the array $row and save to file with filehandle $fp */
$line = getcsvline( $row, ",", "\"", "\r\n" );
fwrite( $fp, $line);
6
Bob Siefkes

vous pouvez également sortir au format natif unix (\ n uniquement) puis exécuter unix2dos sur le fichier résultant pour le convertir en\r\n aux endroits appropriés. Faites juste attention à ce que vos données ne contiennent aucun\n. De plus, je vois que vous utilisez un séparateur par défaut de ~. essayez un séparateur par défaut de\t.

2
Zak

Comme webbiedave l'a souligné (thx!), Le moyen le plus propre est probablement d'utiliser un filtre de flux.

C'est un peu plus complexe que d'autres solutions, mais fonctionne même sur des flux qui ne sont pas modifiables après leur écriture (comme un téléchargement en utilisant $handle = fopen('php://output', 'w');)

Voici mon approche:

class StreamFilterNewlines extends php_user_filter {
    function filter($in, $out, &$consumed, $closing) {

        while ( $bucket = stream_bucket_make_writeable($in) ) {
            $bucket->data = preg_replace('/([^\r])\n/', "$1\r\n", $bucket->data);
            $consumed += $bucket->datalen;
            stream_bucket_append($out, $bucket);
        }
        return PSFS_PASS_ON;
    }
}

stream_filter_register("newlines", "StreamFilterNewlines");
stream_filter_append($handle, "newlines");

fputcsv($handle, $list, $seperator, $Enclosure);
...
2
Torge

J'ai eu affaire à une situation similaire. Voici une solution que j'ai trouvée qui génère des fichiers CSV avec des fins de ligne conviviales pour Windows.

http://www.php.net/manual/en/function.fputcsv.php#9088

Je n'ai pas pu utiliser le car j'essaie de diffuser un fichier sur le client et je ne peux pas utiliser les fseeks.

1
PPC-Coder