Как определить разделитель в строке в PHP?
Мне любопытно, если у вас есть строка, как бы вы обнаружили разделитель?
Мы знаем, что php может разбить строку с помощью explode(), для которой требуется параметр разделителя.
Но как насчет метода определения разделителя перед отправкой функции разнесения?
Прямо сейчас я просто вывожу строку пользователю, и он вводит разделитель. Это нормально - но я ищу приложение для распознавания образов для меня.
Стоит ли искать регулярные выражения для этого типа распознавания образов в строке?
РЕДАКТИРОВАТЬ: я не смог изначально указать, что есть вероятный ожидаемый набор разделителей. Любой разделитель, который, вероятно, используется в CSV. Технически, любой может использовать любой символ для разграничения файла CSV, но более вероятно использование одного из следующих символов: запятая, точка с запятой, вертикальная черта и пробел.
РЕДАКТИРОВАТЬ 2: Вот работоспособное решение, которое я придумал для "определенного разделителя".
$get_images = "86236058.jpg 86236134.jpg 86236134.jpg";
//Detection of delimiter of image filenames.
$probable_delimiters = array(",", " ", "|", ";");
$delimiter_count_array = array();
foreach ($probable_delimiters as $probable_delimiter) {
$probable_delimiter_count = substr_count($get_images, $probable_delimiter);
$delimiter_count_array[$probable_delimiter] = $probable_delimiter_count;
}
$max_value = max($delimiter_count_array);
$determined_delimiter_array = array_keys($delimiter_count_array, max($delimiter_count_array));
while( $element = each( $determined_delimiter_array ) ){
$determined_delimiter_count = $element['key'];
$determined_delimiter = $element['value'];
}
$images = explode("{$determined_delimiter}", $get_images);
5 ответов
Определите, какие разделители вы считаете вероятными (например, ,
, ;
а также |
) и для каждого поиска, как часто они встречаются в строке (substr_count
). Затем выберите тот, который встречается чаще всего, в качестве разделителя и explode
,
Даже если это не является отказоустойчивым, в большинстве случаев это должно сработать;)
Я бы сказал, что это работает в 99,99% случаев:) Основная идея заключается в том, что количество допустимых разделителей должно быть одинаковым построчно. Этот скрипт вычисляет расхождения в количестве разделителей между всеми строками. Меньшее расхождение означает более вероятный действительный разделитель.
Собрав все это вместе, эта функция считывает строки и возвращает их обратно в виде массива:
function readCSV($fileName)
{
//detect these delimeters
$delA = array(";", ",", "|", "\t");
$linesA = array();
$resultA = array();
$maxLines = 20; //maximum lines to parse for detection, this can be higher for more precision
$lines = count(file($fileName));
if ($lines < $maxLines) {//if lines are less than the given maximum
$maxLines = $lines;
}
//load lines
foreach ($delA as $key => $del) {
$rowNum = 0;
if (($handle = fopen($fileName, "r")) !== false) {
$linesA[$key] = array();
while ((($data = fgetcsv($handle, 1000, $del)) !== false) && ($rowNum < $maxLines)) {
$linesA[$key][] = count($data);
$rowNum++;
}
fclose($handle);
}
}
//count rows delimiter number discrepancy from each other
foreach ($delA as $key => $del) {
echo 'try for key=' . $key . ' delimeter=' . $del;
$discr = 0;
foreach ($linesA[$key] as $actNum) {
if ($actNum == 1) {
$resultA[$key] = 65535; //there is only one column with this delimeter in this line, so this is not our delimiter, set this discrepancy to high
break;
}
foreach ($linesA[$key] as $actNum2) {
$discr += abs($actNum - $actNum2);
}
//if its the real delimeter this result should the nearest to 0
//because in the ideal (errorless) case all lines have same column number
$resultA[$key] = $discr;
}
}
var_dump($resultA);
//select the discrepancy nearest to 0, this would be our delimiter
$delRes = 65535;
foreach ($resultA as $key => $res) {
if ($res < $delRes) {
$delRes = $res;
$delKey = $key;
}
}
$delimeter = $delA[$delKey];
echo '$delimeter=' . $delimeter;
//get rows
$row = 0;
$rowsA = array();
if (($handle = fopen($fileName, "r")) !== false) {
while (($data = fgetcsv($handle, 1000, $delimeter)) !== false) {
$rowsA[$row] = Array();
$num = count($data);
for ($c = 0; $c < $num; $c++) {
$rowsA[$row][] = trim($data[$c]);
}
$row++;
}
fclose($handle);
}
return $rowsA;
}
У меня та же проблема, я имею дело со многими CSV из разных баз данных, которые разные люди извлекают в CSV различными способами, иногда по-разному каждый раз для одного и того же набора данных... Просто реализовали такую функцию в моей базе конвертации учебный класс
protected function detectDelimiter() {
$handle = @fopen($this->CSVFile, "r");
if ($handle) {
$line=fgets($handle, 4096);
fclose($handle);
$test=explode(',', $line);
if (count($test)>1) return ',';
$test=explode(';', $line);
if (count($test)>1) return ';';
//.. and so on
}
//return default delimiter
return $this->delimiter;
}
Я сделал что-то вроде этого:
$line = fgetcsv($handle, 1000, "|");
if (isset($line[1]))
{
echo "delimiter is: |";
$delimiter="|";
}
else
{
$line1 = fgetcsv($handle, 1000, ";");
if (isset($line1[1]))
{
echo "delimiter is: ;";
$delimiter=";";
}
else
{
echo "delimiter is: ,";
$delimiter=",";
}
}
Это просто проверяет, есть ли второй столбец после чтения строки.
У меня та же проблема. Моя система будет получать CSV-файлы от клиента, но она может использовать ";", "," или "" в качестве разделителя, и я хочу улучшить систему, чтобы клиент не знал, что это такое (они никогда этого не делают).
Я ищу и нашел эту библиотеку: https://github.com/parsecsv/parsecsv-for-php
Очень хороший и простой в использовании.