У меня есть файл CSV, содержащий около 200 000 - 300 000 записей. Большинство записей можно разделить и вставить в базу данных MySQL с помощью простого
$line = explode("\n", $fileData);
а затем значения, разделенные
$lineValues = explode(',', $line);
а затем вставляется в базу данных с использованием правильного типа данных, то есть int, float, string, text и т. д.
Однако в некоторых записях есть текстовый столбец, в строке которого есть \ n. Что ломается при использовании $ line = explode ("\ n", $ fileData); метод. Каждая строка данных, которую необходимо вставить в базу данных, содержит примерно 216 столбцов. не в каждой строке есть запись с \ n в строке. Однако каждый раз, когда в строке встречается \ n, он заключен в пару одинарных кавычек (')
каждая строка настроена в следующем формате:
id,data,data,data,text,more data
пример:
1,0,0,0,'Hello World,0
2,0,0,0,'Hello
World',0
3,0,0,0,'Hi',0
4,0,0,0,,0
Как видно из примера, большинство записей можно легко разделить с помощью методов, показанных выше. Это вторая запись в примере, которая вызывает проблему.
Новые строки - это только \ n, и файл вообще не включает \ r в файл.






Если вы можете быть уверены, что каждая новая строка, начинающаяся с числа, является допустимой новой строкой (то есть не в середине текстового описания), вы можете попробовать что-то вроде следующего:
// Replace all new-line then id patterns with new-line 0+id
$line = preg_replace('/\n(\d)/',"\n0",$line);
// Split on new-line then id
$linevalues = preg_split("/\n\d/",$data);
Первый шаг идентифицирует все строки, у которых есть новая строка, за которой следует числовое значение. Затем к этому числовому значению добавляется «0». Вторая строка разделяется там, где сначала появляется новая строка, а затем целое число.
«0» добавляется перед идентификатором, поскольку preg_split удаляет совпадающие символы из последующих совпадений.
Как я уже сказал, это сработает только в том случае, если вы уверены, что текст, разрывающий строку, не будет начинать новую строку с числа.
Если данные csv находятся в файле, вы можете просто использовать fgetcsv (), как указывали другие. fgetcsv правильно обрабатывает встроенные символы новой строки.
Однако, если ваши данные csv находятся в строке (например, $ fileData в вашем примере), следующий метод может быть полезен, поскольку str_getcsv () работает только с строкой за раз и не может разбить весь файл на записи.
Вы можете обнаружить встроенные символы новой строки, посчитав кавычки в каждой строке. Если количество кавычек нечетное, значит, у вас неполная строка, поэтому объедините эту строку со следующей строкой. Как только у вас будет четное количество цитат, у вас будет полная запись.
Как только у вас будет полная запись, разделите ее на кавычки (снова используя explode ()). Поля с нечетными номерами заключаются в кавычки (таким образом, встроенные запятые не являются специальными), а поля с четными номерами - нет.
Пример:
# Split file into physical lines (records may span lines)
$lines = explode("\n", $fileData);
# Re-assemble records
$records = array ();
$record = '';
$lineSep = '';
foreach ($lines as $line) {
# Escape @ symbol so we can use it as a marker (as it does not conflict with
# any special CSV character.)
$line = str_replace('@', '@a', $line);
# Escape commas as we don't yet know which ones are separators
$line = str_replace(',', '@c', $line);
# Escape quotes in a form that uses no special characters
$line = str_replace("\'", '@q', $line);
$line = str_replace('\', '@b', $line);
$record .= $lineSep . $line;
$lineSep = "\n";
# Must have an even number of quotes in a complete record!
if (substr_count($record, "'") % 2 == 0) {
$records[] = $record;
$record = '';
$lineSep = '';
}
}
if (strlen($record) > 0) {
$records[] = $record;
}
$rows = array ();
foreach ($records as $record) {
$chunks_in = explode("'", $record);
$chunks_out = array ();
# Decode escaped quotes/backslashes.
# Decode field-separating commas (unless quoted)
foreach ($chunks_in as $i => $chunk) {
# Unescape quotes & backslashes
$chunk = str_replace('@q', "'", $chunk);
$chunk = str_replace('@b', '\', $chunk);
if ($i % 2 == 0) {
# Unescape commas
$chunk = str_replace('@c', ',', $chunk);
}
$chunks_out[] = $chunk;
}
# Join back together, discarding unescaped quotes
$record = join('', $chunks_out);
$chunks_in = explode(',', $record);
$row = array ();
foreach ($chunks_in as $chunk) {
$chunk = str_replace('@c', ',', $chunk);
$chunk = str_replace('@a', '@', $chunk);
$row[] = $chunk;
}
$rows[] = $row;
}
как насчет ручного перебора данных от начала до конца с одним или двумя циклами for? Он медленнее, чем explode(), но по котировкам легче получить последовательные и надежные результаты.
Если вы выберете этот метод, не забудьте принять во внимание экранированные кавычки.
Другой совет здесь, конечно, действителен, особенно если вы хотите написать свой собственный анализатор CSV, однако, если вы просто хотите получить данные, используйте функцию fgetcsv () и не беспокойтесь о деталях реализации.
Используйте fgetcsv, и он позаботится обо всем за вас. Если нет какой-либо основной причины, вам нужен собственный парсер CSV.
Я не знаком с функцией fgetcsv (). Это первый раз, когда мне было поручено взять около 300 МБ файлов csv и вставить их в базу данных MySQL. Первые несколько файлов были легкими, поскольку в них не было встроенных новых строк.