У меня есть следующая функция, чтобы получить все различные типы разделителей конца строки в файле. Их может быть один или несколько, поэтому я хочу вернуть массив всех типов.
function ddtt_get_file_eol( $file_contents, $incl_code = true ) {
$types = [
'\r\n',
'\n',
'\r'
];
$found = [];
foreach ( $types as $type ) {
if ( $type == '\r\n' ) {
$regex = "/\r\n/";
} elseif ( $type == '\n' ) {
$regex = "/(?<!\r)\n/";
} else {
$regex = "/\r(?!\n)/";
}
if ( preg_match( $regex, $file_contents ) ) {
$found[] = ( $incl_code ) ? '<code class = "hl">'.$type.'</code>' : $type;
}
}
return $found;
} // End ddtt_get_php_eol()
Проблема, с которой я столкнулся, заключается в том, что он распознает \r\n
как два отдельных типа и выводит [ '\n', '\r' ]
. Я хочу вывести [ '\r\n' ]
, если он использует только этот тип, или [ '\r\n', '\n' ]
, если используются оба типа и т. д. Как мне изменить свой код, чтобы правильно получать все используемые типы?
Такой результат может получиться, если файл представляет собой смесь обоих типов окончания строк. К сожалению, у меня было такое несколько раз, когда я работал над некоторыми крупными проектами на Git, когда пользователи работали в Windows, другие — в Linux, и репозиторий Git не был настроен должным образом для автоматического преобразования окончаний строк при коммитах. Будет ли этот результат получен, если вы отредактируете несколько тестовых файлов, в которых используете только один тип окончания строк?
Я не могу воспроизвести проблему. Когда я звоню ddtt_get_file_eol("foo\r\nbar\r\n", false)
, я получаю только ["\r\n"]
. Предоставьте образец входных данных, который дал неверный результат.
@PatrickJanser Им нужен такой результат, если в файле есть разные разрывы строк. Вот почему он возвращает массив.
@Barmar: Да, я понял, что этого хочет Аристокл. Но мой вопрос состоит в том, чтобы убедиться, что он действительно тестирует файл, имеющий оба окончания строк, и что это не может быть ошибкой в его функции.
Точно. Вот почему я попросил образец ввода.
Ладно, я слишком долго шел к этому и не думал возвращаться к основам. Я протестировал код с тем, что использовал @Barmar, и он отлично работает во всех случаях. Значит, с моим кодом что-то еще не так. Я пытаюсь обновить файл, который разбивает строки на массив, обновляет определенные строки, а затем объединяет их обратно в строку. При этом я использую это, чтобы определить, какой разделитель eol используется, чтобы я мог поддерживать те же разделители, но выдавать предупреждение, если используются смешанные eol-разделители. Если это имеет смысл.
Спасибо @PatrickJanser за предложение по вводу массива. Изначально я так и сделал, и на протяжении всего тестирования я просто делал оператор if и не возвращался назад. 👍
В Википедии есть страница о различных стилях новой строки, на ней есть LF-CR, которого нет в вашем списке. Несколько лет назад я использовал систему, использующую CR-CR-LF, ее тоже нет в вашем списке. Чтобы частично ответить на ваш вопрос, я бы поискал вхождения [\r\n]+
и добавил их в список $types
.
Честно говоря, если ваш ввод смешивает разные типы окончания строк, это проблема, которую нужно решить, в идеале в источнике, а не танцевать в коде.
@Sammitch: к сожалению, это случается чаще, чем можно было бы подумать. Его инструмент мог бы эффективно возвращать результат сразу после первого совпадения, а не проверять все случаи, чтобы быть более эффективным. Но это было бы неправильно. Тестирование всех типов окончания строк кажется хорошим способом сделать это. Даже такие инструменты Linux, как file
, вернут информацию о нескольких типах окончания строк, если они смешаны.
@Саммитч, верно, поэтому я обнаруживаю это и предупреждаю. Чего я не упомянул, так это того, что когда я соберу его обратно, он будет использовать только один тип, указанный пользователем, но будет рекомендовать текущее значение PHP_EOL
, если пользователь не знает, какой из них выбрать.
@Aris Я не понимаю, почему мой ответ помечен как бесполезный, но если вы собираетесь очистить и обеспечить согласованность последовательностей новой строки, \R
- это именно то решение. Сопоставьте все символы \R
, затем замените их предпочтительной последовательностью новой строки — готово и без каких-либо ошибок.
На мой взгляд, ваш код в порядке. Это просто ваш вклад, который представляет собой смесь.
<?php
$LINE_TYPES = [
'\\r\\n' => '/\\r\\n/',
'\\n' => '/(?<!\\r)\\n/',
'\\r' => '/\\r(?!\\n)/',
];
$inputs = [
'Windows' => "Dog\r\nCat\r\nMouse",
'Linux' => "Bicycle\nCar\nTrain\nAirplane",
'Mac' => "iPhone\riPod\rMacBook",
'Win + Linux' => "int main() {\n return 0;\r\n}\n",
'All mixed up' => "This is a Windows new line\r\n, followed by a Linux new line\n and finally an old Mac with a single carriage return\rat the end",
];
foreach ($inputs as $label => $input) {
$found_types = [];
foreach ($LINE_TYPES as $type => $regex) {
if (preg_match($regex, $input)) {
$found_types[] = $type;
}
}
print "Found types for $label is " . implode(', ', $found_types) . PHP_EOL;
}
Выводит следующее:
Found types for Windows is \r\n
Found types for Linux is \n
Found types for Mac is \r
Found types for Win + Linux is \r\n, \n
Found types for All mixed up is \r\n, \n, \r
что кажется совершенно нормальным.
Вы можете протестировать/поиграть с ним здесь: https://onlinephp.io/c/4ce47
Дайте угадаю, вы разработчик, которому нужна идеальная идентификация последовательностей новой строки независимо от среды, И вы хотите сохранить все свои волосы?
В PHP уже давно есть решение этой проблемы, и оно не связано с миноксидилом; просто используйте \R
. Я заменю каждую последовательность новой строки звездочкой, чтобы показать, как она надежно учитывает все возможные последовательности новой строки во всех средах и обрабатывает их как целые последовательности новой строки, когда это необходимо.
Код: (Демо)
$inputs = [
'Windows' => "Dog\r\nCat\r\nMouse",
'Linux' => "Bicycle\nCar\nTrain\nAirplane",
'Mac' => "iPhone\riPod\rMacBook",
'Win + Linux' => "int main() {\n return 0;\r\n}\n",
'All mixed up' => "This is a Windows new line\r\n, followed by a Linux new line\n and finally an old Mac with a single carriage return\rat the end",
];
var_export(
preg_replace('/\R/', '*', $inputs)
);
Выход:
array (
'Windows' => 'Dog*Cat*Mouse',
'Linux' => 'Bicycle*Car*Train*Airplane',
'Mac' => 'iPhone*iPod*MacBook',
'Win + Linux' => 'int main() {* return 0;*}*',
'All mixed up' => 'This is a Windows new line*, followed by a Linux new line* and finally an old Mac with a single carriage return*at the end',
)
Если вам нужен массив последовательностей новой строки, просто используйте preg_match_all()
с тем же единственным шаблоном. Демо
foreach ($inputs as $env => $input) {
preg_match_all('/\R/', $input, $matches);
var_dump(
$env,
json_encode($matches[0])
);
}
Соответствующее чтение о реализации \R
:
Я не ДВ, но как это отвечает на вопрос? Они хотят обнаружить, когда в документе обнаружено несколько стилей новой строки. Они не хотят заменять символы новой строки чем-либо.
Я также покажу, как сопоставить последовательности новой строки. Я не понимаю, как это не решает эту (xy) проблему. Спрашивающий говорит: «Проблема, с которой я столкнулся, заключается в том, что он распознает \r\n как два отдельных типа» — \R
— это самый прямой и элегантный способ решить эту проблему.
@mickmackusa, я только сейчас вижу, что ты ответил. Я тоже не минусовал ваш ответ. Кто-то тоже проголосовал против моего вопроса.
@ Аристокл, я считаю, что мое решение - самое лучшее решение для твоей задачи. Вы надеетесь стандартизировать свой текст, верно? Вам не нужно сканировать весь документ на наличие вариантов перевода строки, просто замените последовательности перевода строки на preg_replace()
и \R
. Этот ответ гораздо более прямой, чем ваш принятый ответ, если я чего-то не понимаю.
@mickmackusa Вы правы. Я уже принял лучший ответ до того, как вы добавили свой, и увидел ваш только вчера вечером около полуночи в мое время, поэтому я не собирался переходить в рабочий режим. Сегодня утром, прочитав ваш ответ, он показался мне лучшим решением, и я принял его как таковое. До сих пор я не знал о \R
. Вся эта чушь с EOL для меня в новинку. XD
Просто замечание к вашему коду: ваш массив типов можно записать как массив с ключами, а не как простой массив. Это позволит избежать этого
if
,elseif
,else
. Ключом может быть тип и значение регулярного выражения.