我需要使用PHP:
从以这种方式格式化的文本文件中提取数据BEGIN
#1
#2
#3
#4
#5
#6
1 2015-05-31 2001-11-24 'Name Surname' ID_1 0
2 2011-04-01 ? ? ID_2 1
2 2013-02-24 ? ? ID_3 1
2 2014-02-28 ? 'Name Surname' ID_4 2
END
信息按照如下数组的逻辑进行组织:
Array ( [#1] => 1 [#2] => 2015-05-31 [#3] => 2001-11-24 [#4] => 'Name Surname' [#5] => ID_1 [#6] => 0 )
Array ( [#1] => 2 [#2] => 2011-04-01 [#3] => ? [#4] => ? [#5] => ID_2 [#6] => 1 )
Array ( [#1] => 2 [#2] => 2013-02-24 [#3] => ? [#4] => ? [#5] => ID_3 [#6] => 1 )
Array ( [#1] => 2 [#2] => 2014-02-28 [#3] => ? [#4] => 'Name Surname' [#5] => ID_4 [#6] => 2 )
我正在寻找获得该输出的方法。我正在使用此代码:
<?php
//ini_set('max_execution_time', 300); //300 seconds = 5 minutes
function startsWith($str, $char){
return $str[0] === $char;
}
$txt_path = "./test.txt";
$txt_data = @file_get_contents($txt_path) or die("Could not access file: $txt_path");
//echo $txt_data;
$loop_pattern = "/BEGIN(.*?)END/s";
preg_match_all($loop_pattern, $txt_data, $matches);
$loops = $matches[0];
//print_r($loops);
$loops_count = count($loops);
//echo $loops_count; // number of loops into the file
foreach ($loops as $key => $value) {
$value = trim($value);
$pattern = array("/[[:blank:]]+/", "/BEGIN(.*)/", "/END(.*)/");
$replacement = array(" ", "", "");
$value = preg_replace($pattern, $replacement, $value);
//print_r($value);
//echo "<br><br>";
$value_array = explode("\n", $value);
$value_array_clean = array_filter($value_array, 'strlen');
$value_array_clean_reindex = array_values($value_array_clean);
//print_r($value_array_clean_reindex);
//echo "<br><br>";
$keys = array();
$values = array();
foreach ($value_array_clean_reindex as $key => $value) {
$value = trim($value);
if ( startsWith($value, "#") ) {
array_push($keys, $value);
$keys_count = count($keys);
} else {
array_push($values, $value);
$values_count = count($values);
$loop_dic = array();
foreach ($values as $key => $value) {
$value = trim($value);
preg_match_all("/'(?:.|[^'])*'|\S+/", $value, $matches);
//print_r($matches[0]);
$loop_dic = array_combine($keys, $matches[0]);
}
print_r($loop_dic);
echo "<br><br>";
}
}
}
?>
它给了我想要的输出:
Array ( [#1] => 1 [#2] => 2015-05-31 [#3] => 2001-11-24 [#4] => 'Name Surname' [#5] => ID_1 [#6] => 0 )
Array ( [#1] => 2 [#2] => 2011-04-01 [#3] => ? [#4] => ? [#5] => ID_2 [#6] => 1 )
Array ( [#1] => 2 [#2] => 2013-02-24 [#3] => ? [#4] => ? [#5] => ID_3 [#6] => 1 )
Array ( [#1] => 2 [#2] => 2014-02-28 [#3] => ? [#4] => 'Name Surname' [#5] => ID_4 [#6] => 2 )
但有时在命令级别出现问题:
$loop_dic = array_combine($keys, $matches[0]);
据我所知,在原始文本文件中,线条很长,这些都被打破了,产生了一条新线;而不是:
2 2014-02-28 ? 'Name Surname' ID_4 2
这条线被打破了:
2 2014-02-28 ? 'Name Surname'
ID_4 2
因此,当我按\n
分解字符串时,它会在我合并的两个数组的长度中出现错误。
我会问你一个替代方法来解决这个问题,获得相等长度的数组,如果在原始文件中发生中断的话。
在网上搜索,我发现array_fill;也许,如果我知道(通过count
)每个循环([#1],...,[#6])的数组中的键数,就可以循环并填充数组的值,按顺序添加它们,直到值的每个数组的最大长度。
感谢您的关注和帮助。
编辑#1
感谢@ fusion3k的解决方案! 使用某些输入文件检查行为会显示另外两个问题:
1)分析一些错误,我发现有时输入文件使用双引号(而不是单引号),并且分号之间的多行文本块,如下所示:
;This is some text
in multiline with "double
quotes" too
;
需要被视为给定键的单个值,其值必须是内联的,如@ fusion3k代码,将\n
替换为(空格)。我正在尝试将@ fusion3k的工作代码与精心设计的代码合并以解决此问题。文件结构可能就像这样:
BEGIN
#1
#2
#3
#4
#5
#6
1 2015-05-31 2001-11-24 "Name Surname" ID_1 0
2 2011-04-01 ? ? ID_2 1
2 2013-02-24 ? ? ID_3 1
2 2014-02-28 ? "Name Surname" ID_4 2
;This is some text
in multiline with "double
quotes" too
;
2016-01-22 ? "Name Surname" ID_5 2
END
应生成类似上述工作代码的内容,但考虑到存在不同的文本块定界符,如分号(;
),单引号( '
)或者像其他文件一样,双引号("
),用于分隔必须被视为键的单个值的文本块,例如这个数组相对于上面的文本文件内容:
Array ( [#1] => Array ( [0] => 1 [1] => 2 [2] => 2 [3] => 2 [4] => This is some text in multiline with "double quotes" too ) [#2] => Array ( [0] => 2015-05-31 [1] => 2011-04-01 [2] => 2013-02-24 [3] => 2014-02-28 [4] => 2016-01-22 ) [#3] => Array ( [0] => 2001-11-24 [1] => ? [2] => ? [3] => ? [4] => ? ) [#4] => Array ( [0] => Name Surname [1] => ? [2] => ? [3] => Name Surname [4] => Name Surname ) [#5] => Array ( [0] => ID_1 [1] => ID_2 [2] => ID_3 [3] => ID_4 [4] => ID_5 ) [#6] => Array ( [0] => 0 [1] => 1 [2] => 1 [3] => 2 [4] => 2 ) )
我在一个简单的字符串上工作,找到一个考虑(分号)AND(单引号 OR 双引号)。目前我还没有找到使用所有三个分隔符来分隔文本块的文件,但似乎可以找到分号 + single_quotes 或分号 + double_quotes 或仅 single_quotes 或仅 double_quotes ;在同一个文本文件中找到包含所有三种分隔符的解决方案......:
$string = 'something here
;and there
;
oh, "that\'s all!"';
$string = str_replace( "\n", " ", $string );
$origin = array("/[[:blank:]]+/", "/\"/", "/;/");
$replacement = array(" ", "\" ", "; ");
$string = preg_replace($origin, $replacement, $string);
$pattern = '/([;"])\s+/';
print_r(array_filter(preg_split( $pattern, $string ), 'strlen'));
这是输出(如所需):
Array ( [0] => something here [1] => and there [2] => oh, [3] => that's all! )
注意分号之间的文本块:它始终以新行开头,开头是分号,并以分号结束在一个新行中,然后开始另一个换行符。
我不知道它是否能以更好,更快的方式编写...我尝试将其与@ fusion3k的代码合并,处理上述文本文件内容,但没有成功。我尝试了像这样的if/elseif/else
结构:
if ( preg_match('/;(.*?);|\'(.*?)\'/', $value, $matches) ) {// semicolon with single quotes in the $value string
$value = str_replace( "\n", " ", $value );
$origin = array("/[[:blank:]]+/", "/'/", "/;/");
$replacement = array(" ", "' ", "; ");
$value = preg_replace($origin, $replacement, $value);
$pattern = '/'.str_repeat( "([;'])\s+", count( $keys ) ).'/';
print_r(array_filter(preg_split( $pattern, $value ), 'strlen')); // I would have an array of values of the same length of the array for the keys
echo "<br><br>";
} elseif ( preg_match('/;(.*?);|"(.*?)"/', $value, $matches) ) {// semicolon with double quotes in the $value string
$value = str_replace( "\n", " ", $value );
$origin = array("/[[:blank:]]+/", "/\"/", "/;/");
$replacement = array(" ", "\" ", "; ");
$value = preg_replace($origin, $replacement, $value);
$pattern = '/'.str_repeat( "([;\"])\s+", count( $keys ) ).'/';
print_r(array_filter(preg_split( $pattern, $value ), 'strlen')); // I would have an array of values of the same length of the array for the keys
echo "<br><br>";
} else {// neither single quotes (or double quotes) nor semicolon in the $value string
$pattern = '/'.str_repeat( "(\S+)\s+", count( $keys ) ).'/';
preg_match_all( $pattern, $value, $matches );
//print_r($matches);
//echo "<br><br>";
$loop_dic = array_combine( $keys, array_slice( $matches, 1 ) );
print_r( $loop_dic ); // this is good...maybe in a better way?
echo "<br><br>";
}
唯一有效的代码是使用@ fusion3k代码的最后一个代码。
2)当文件非常大时,会发生第二种行为(可能已经解决)。命令:
$loop_pattern = "/BEGIN(.*?)END/s";
preg_match_all($loop_pattern, $txt_data, $matches);
$loops = $matches[0];
//print_r($loops);
$loops_count = count($loops);
//echo $loops_count; // number of loops into the file
不会占用文件中的所有循环(大文件)。 我想答案可能是here。所以,设置:
ini_set('max_execution_time', 300); // 300 seconds = 5 minutes
ini_set("pcre.backtrack_limit", "100000000"); // default 100k = "100000"
似乎解决了这个问题,但我不知道这是否是唯一的方法:确实,如果文件很大(17MB或更高),浏览器的响应时间会有一点(我在Firefox上测试)最新的),在页面加载完成之前...将整个文件解析成块,直到它的完整大小,但是,如何做到这一点?
非常感谢您的关注和帮助
答案 0 :(得分:1)
要解决您的问题,通常的方法是计算检索到的匹配项 - 如果它们小于键 - 继续循环而不重新初始化$loop_dic
。
我建议你使用倒置方法:不是逐行爆炸,而是在检索值之前用空格替换换行:你的字符串结构足够坚固以允许这种方法,你知道字段号,所以这种方法应该有效。
主foreach
循环外的代码不会发生变化。以同样的方式,检索由BEGIN ... END
包装的文本的代码不受影响:
foreach( $loops as $key => $value )
{
$value = trim( $value );
$pattern = array( "/[[:blank:]]+/", "/BEGIN(.*)/", "/END(.*)/" );
$replacement = array( " ", "", "" );
$value = preg_replace( $pattern, $replacement, $value );
要检索密钥,我们使用preg_match_all()
,然后删除preg_replace()
的相对行:
preg_match_all( '/^#\d+/m', $value, $matches );
$keys = $matches[0];
$value = preg_replace( '/^#\d+\s*/m', '', $value );
现在,$value
我们只有数据线。我们用空格替换所有换行符:
$value = str_replace( "\n", " ", $value );
然后,我们通过重复键编号的字段模式来构造行模式,并按preg_match_all()
检索所有行:
$pattern = '/'.str_repeat( "('[^']+'|\S+)\s+", count( $keys ) ).'/';
preg_match_all( $pattern, $value, $matches );
最后,我们使用array_slice()
删除全局匹配项,我们将其与$keys
结合使用,我们得到了期望的结果。可以关闭foreach
循环:
$values = array_combine( $keys, array_slice( $matches, 1 ) );
}
的 ideone demo 强>
我的$values
与您的$loop_dic
之间的主要区别在于$values
主阵列中有列,但如果您喜欢按行排列数组,则可以轻松转换它。 / p>
我已经使用许多不同的“断线”测试了代码,并且它可以工作。我建议你用不同的字符串仔细测试它,看看它在任何情况下都能正常工作。