Определить тип EOL с помощью PHP

Ссылка: Это вопрос, ответивший на вопрос. Он должен был делиться знаниями, стилем Q & A.

Как определить тип символа конец строки в PHP?

PS: Я писал этот код с нуля слишком долго, поэтому я решил поделиться им с SO, плюс, я уверен, что кто-то найдет способы для улучшения.

Ответ 1

/**
 * Detects the end-of-line character of a string.
 * @param string $str The string to check.
 * @param string $default Default EOL (if not detected).
 * @return string The detected EOL, or default one.
 */
function detectEol($str, $default=''){
    static $eols = array(
        "\0x000D000A", // [UNICODE] CR+LF: CR (U+000D) followed by LF (U+000A)
        "\0x000A",     // [UNICODE] LF: Line Feed, U+000A
        "\0x000B",     // [UNICODE] VT: Vertical Tab, U+000B
        "\0x000C",     // [UNICODE] FF: Form Feed, U+000C
        "\0x000D",     // [UNICODE] CR: Carriage Return, U+000D
        "\0x0085",     // [UNICODE] NEL: Next Line, U+0085
        "\0x2028",     // [UNICODE] LS: Line Separator, U+2028
        "\0x2029",     // [UNICODE] PS: Paragraph Separator, U+2029
        "\0x0D0A",     // [ASCII] CR+LF: Windows, TOPS-10, RT-11, CP/M, MP/M, DOS, Atari TOS, OS/2, Symbian OS, Palm OS
        "\0x0A0D",     // [ASCII] LF+CR: BBC Acorn, RISC OS spooled text output.
        "\0x0A",       // [ASCII] LF: Multics, Unix, Unix-like, BeOS, Amiga, RISC OS
        "\0x0D",       // [ASCII] CR: Commodore 8-bit, BBC Acorn, TRS-80, Apple II, Mac OS <=v9, OS-9
        "\0x1E",       // [ASCII] RS: QNX (pre-POSIX)
        //"\0x76",       // [?????] NEWLINE: ZX80, ZX81 [DEPRECATED]
        "\0x15",       // [EBCDEIC] NEL: OS/390, OS/400
    );
    $cur_cnt = 0;
    $cur_eol = $default;
    foreach($eols as $eol){
        if(($count = substr_count($str, $eol)) > $cur_cnt){
            $cur_cnt = $count;
            $cur_eol = $eol;
        }
    }
    return $cur_eol;
}

Примечания:

  • Необходимо проверить тип кодировки
  • Нужно как-то знать, что мы можем быть в экзотической системе, такой как ZX8x (поскольку ASCII x76 является регулярной буквой) @radu поднял хороший момент, в моем случае это не стоит усилий хорошо управляйте системами ZX8x.
  • Должен ли я разделить функцию на два? mb_detect_eol() (многобайтовые) и detect_eol()

Ответ 2

Не было бы проще просто заменить все, кроме новых строк с использованием regex?

Точка соответствует одному символу, не заботясь о том, что это за символ. Единственное исключение - символы новой строки.

Имея это в виду, мы делаем магию:

$string = 'some string with new lines';
$newlines = preg_replace('/.*/', '', $string);
// $newlines is now filled with new lines, we only need one
$newline = substr($newlines, 0, 1);

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

enter image description here

Ответ 3

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

Он предоставляет ссылку найденного EOL Обнаружение устанавливает также ключ, который может использоваться приложением к этой ссылке. Он показывает, как использовать ссылку в классе утилиты. Показывает, как использовать его для обнаружения файла, возвращающего имя ключа найденного EOL. Я надеюсь, что это будет полезно для всех вас.
/**
Newline characters in different Operating Systems
The names given to the different sequences are:
============================================================================================
NewL  Chars       Name     Description
----- ----------- -------- ------------------------------------------------------------------
LF    0x0A        UNIX     Apple OSX, UNIX, Linux
CR    0x0D        TRS80    Commodore, Acorn BBC, ZX Spectrum, TRS-80, Apple II family, etc
LFCR  0x0A 0x0D   ACORN    Acorn BBC and RISC OS spooled text output.
CRLF  0x0D 0x0A   WINDOWS  Microsoft Windows, DEC TOPS-10, RT-11 and most other early non-Unix
                          and non-IBM OSes, CP/M, MP/M, DOS (MS-DOS, PC DOS, etc.), OS/2,
----- ----------- -------- ------------------------------------------------------------------
*/
const EOL_UNIX    = 'lf';        // Code: \n
const EOL_TRS80   = 'cr';        // Code: \r
const EOL_ACORN   = 'lfcr';      // Code: \n \r
const EOL_WINDOWS = 'crlf';      // Code: \r \n

затем используйте следующий код в статическом классе Утилита для обнаружения

/**
Detects the end-of-line character of a string.
@param string $str      The string to check.
@param string $key      [io] Name of the detected eol key.
@return string The detected EOL, or default one.
*/
public static function detectEOL($str, &$key) {
   static $eols = array(
     Util::EOL_ACORN   => "\n\r",  // 0x0A - 0x0D - acorn BBC
     Util::EOL_WINDOWS => "\r\n",  // 0x0D - 0x0A - Windows, DOS OS/2
     Util::EOL_UNIX    => "\n",    // 0x0A -      - Unix, OSX
     Util::EOL_TRS80   => "\r",    // 0x0D -      - Apple ][, TRS80
  );

  $key = "";
  $curCount = 0;
  $curEol = '';
  foreach($eols as $k => $eol) {
     if( ($count = substr_count($str, $eol)) > $curCount) {
        $curCount = $count;
        $curEol = $eol;
        $key = $k;
     }
  }
  return $curEol;
}  // detectEOL

а затем для файла:

/**
Detects the EOL of an file by checking the first line.
@param string  $fileName    File to be tested (full pathname).
@return boolean false | Used key = enum('cr', 'lf', crlf').
@uses detectEOL
*/
public static function detectFileEOL($fileName) {
   if (!file_exists($fileName)) {
     return false;
   }

   // Gets the line length
   $handle = @fopen($fileName, "r");
   if ($handle === false) {
      return false;
   }
   $line = fgets($handle);
   $key = "";
   <Your-Class-Name>::detectEOL($line, $key);

   return $key;
}  // detectFileEOL

Измените имя своего класса на свое имя для класса реализации (все статические члены).

Ответ 4

Мой ответ, потому что я не могу сделать ohaal один или transilvlad один труд, является:

function detect_newline_type($content) {
    $arr = array_count_values(
               explode(
                   ' ',
                   preg_replace(
                       '/[^\r\n]*(\r\n|\n|\r)/',
                       '\1 ',
                       $content
                   )
               )
           );
    arsort($arr);
    return key($arr);
}

Объяснение:

Общая идея в обоих предлагаемых решениях хороша, но детали реализации затрудняют полезность этих ответов.

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

Это само по себе делает неправильным использование str_split(). Единственный способ правильно разрезать маркеры - использовать функцию, которая режет строку с переменной длиной, на основе обнаружения символов. То есть, когда explode() вступает в игру.

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

Необходимо учитывать 3 точки:

  • с помощью .*, как предложено ohaal, не будет работать. Хотя верно, что . не будет соответствовать символам новой строки, в системе, где \r не является символом новой строки или частью символа новой строки, . будет соответствовать ему неправильно (напоминание: мы обнаруживаем новые строки, потому что они может отличаться от тех, что есть в нашей системе, иначе нет смысла).
  • заменив /[^\r\n]*/ на что-нибудь, "будет работать", чтобы текст исчез, но будет проблемой, как только мы захотим иметь разделитель (поскольку мы удаляем все символы, кроме строк новой строки, любой символ, который не является newline будет действительным разделителем). Следовательно, идея создания соответствия с новой строкой и использование обратной ссылки для этого совпадения в замене.
  • Возможно, что в контенте несколько строк новой строки будут в строке. Однако мы не хотим группировать их в этом случае, так как они будут рассматриваться остальной частью кода как разные типы новых строк. Вот почему список новых строк явно указан в матче для обратной ссылки.

Ответ 5

Основываясь на ohaal ответе.

Это может вернуть один или два символа для EOL, например LF, CR + LF..

  $eols = array_count_values(str_split(preg_replace("/[^\r\n]/", "", $string)));
  $eola = array_keys($eols, max($eols));
  $eol = implode("", $eola);

Ответ 6

Интересная тема и интересное обсуждение. Любопытно, что если бы у нас был случай, когда реальный EOL состоит из двух символов (например, CR + LF), но в другом месте документа встречается одиночный CR или LF. Тогда этот одинокий персонаж будет иметь более высокий счет вхождения, чем настоящий EOL. Разве мы не должны, в этом случае, иметь способ отдавать приоритет двухсимвольному решению, даже если один символ имеет большее число? Убей меня, если я далеко от базы; У меня толстая кожа. :-)