检测csv文件的行结束字符

时间:2017-07-24 20:37:14

标签: php csv line-endings

我需要检测csv文件中的哪种行结尾:

  • \n(UNIX默认值)
  • \r(Mac Excel)
  • \r\n(Windows)
  • 或其他任何东西

获取除界,封闭和放大转义字符,我使用SplFileObject::getCsvControl - 对于结束字符的行来说会很棒。

打开文件

3 个答案:

答案 0 :(得分:2)

我没有尝试过这个,但我认为这是一个有趣的问题所以这是我对可能的解决方案的抨击:

// first, have PHP auto-detect the line endings, like @AbraCadaver suggested:
ini_set("auto_detect_line_endings", true);

// now open the file and read a single line from it
$file = fopen('/path/to/file.csv', 'r');
fgets($file);

// fgets() moves the pointer, so get the current position
$position = ftell($file);

// now get a couple bytes (here: 10) from around that position
fseek($file, $position - 5);
$data = fread($file, 10);

// we no longer need the file
fclose($file);

// now find out how many of each type EOL there are in those 10 bytes
// expected result is that two of these will be 0 and one will be 1
$eols = array(
    "\r\n" => substr_count($data, "\r\n"),
    "\r" => substr_count($data, "\r"),
    "\n" => substr_count($data, "\n"),
);

// sort the EOL count in reverse order, so that the EOL with the highest
// count (expected: 1) will be the first item
arsort($eols);

// get the first item's key
$eol = key($eols);

// $eol will now be "\r\n", "\r" or "\n"

可能有更好的方法,请注意我在这里对您的CSV文件做了一些假设:

  • 文件不以空行开头;
  • 第一行至少5个字节;
  • 第二行不为空,也至少5个字节;
  • 第一行的最后一列和最后一行的第一列不包含任何换行符;
  • 您没有处理具有混合行结尾的文件。

如果您不能指望这些条件,则必须添加一些验证步骤,例如检查fgets()的结果是否实际上是一个包含多个字符的字符串。如果行可能短于5个字节,您可能还必须考虑到结束的行可能\r\n这一事实,但通过寻找原始字节,我们碰巧最终得到了像"abcde\r\nfg\r"这样的字符串,我们错过了第二个\n,你会得到错误的结果。

但是如果你能确定CSV文件的结构,这可能是一个(肮脏的,我会承认)朝着正确方向迈出的一步。

答案 1 :(得分:0)

这是一个有趣的问题 - 没有人能够在这里为您提供完整的解决方案。显而易见的方法是:

1)继续读取文件,直到第一次出现\ r或\ n。在前者的情况下,再读一个字符以检查它是否后跟\ n。

这听起来很简单 - 但您需要实施报价处理以确定EOL是否嵌入在引用的数据字段中 - 并且您不知道如何引用数据。除了检测开始和结束报价之外,您还需要能够确定引用字符是否被转义 - 并且至少有两种不同的方式来转义引号字符。

2)分析文件中字符的频率。如果你可以忽略空格,alhpa字符和数字,那么最常用的余数应该是CSV元字符。但是那些不能用于非常短的文件的人。

3)在文件中创建数据字符串的表示,并查找记录模式,例如如果你发现数字,空间,alpha,空间,数字,标点符号,数字,spache,alpha,标点符号,alpha,空格,数字,标点符号,数字,空格,alpha,空格,数字,标点符号那么你可能会认为字段分隔符是一个空格,记录由标点符号分隔,标点符号也可以显示为嵌入字符。

但这需要一些非常复杂的代码。

如果是我,我只会询问提供文件的人是否提供文件格式的详细信息。或者,如果该信息不可用,则使用十六进制编辑器打开文件。

答案 2 :(得分:0)

我使用了@rickdenhaan解决方案,但发现arsort()和PHP版本存在问题。

如果eol为“ \ r \ n”,则$ eols数组为:

array(“ \ r \ n” => 1,“ \ r” => 1,“ \ n” => 1);

(因为除了找到1个“ \ r \ n”,还发现了1个“ \ r”和1个“ \ n”)

PHP 7 ,在arsort($ eols)之后,键顺序是相同的:

array(“ \ r \ n” => 1,“ \ r” => 1,“ \ n” => 1);

并在“ $ eol = key($ eols);”之后$ eol将为“ \ r \ n”

但是使用 PHP 5.6 ,在arsort($ eols)之后,键顺序为:

array(“ \ n” => 1,“ \ r” => 1,“ \ r \ n” => 1);

并在“ $ eol = key($ eols);”之后$ eol将为“ \ n”

我已经在“ $ eol = key($ eols);”之后解决了此检查:

if (($eols["\r\n"] == $eols["\r"]) AND ($eols["\r\n"] == $eols["\n"])) {
    $line_separator = "\r\n";
}