PHP - 按顺序填充数组,直到达到最大长度
PHP - Fill the arrays in a sequential manner until reaching a maximum length
我需要从以这种方式格式化的文本文件中提取数据,使用 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
替换为 </code>(一个space)。我正在尝试将 @fusion3k 的工作代码与为解决此行为而精心设计的代码合并。文件结构可能是这样的:</p>
<pre><code>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
应该会生成类似于上面的工作代码,但考虑到存在不同的文本块分隔符,例如 semicolon (;
), 单引号 ('
) 或像在某些其他文件中一样,双引号 ("
),以分隔必须被认为是一个键的单个值,就像在这个数组中相对于上面的文本文件内容:
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 ) )
我处理了一个简单的字符串,以找到考虑 (分号) 和 (单引号 的 "working" 正则表达式] 或 双引号 )。现在我还没有找到使用所有三个定界符来定界文本块的文件,但似乎可以找到 semicolon+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>";
}
唯一有效的代码是最后一个 else,它使用了 @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 上是最新的),在页面加载完成之前......最好分块解析整个文件直到它的完整大小,也许,但如何去做?
非常感谢您的关注和帮助
要解决您的问题,常见的方法是对检索到的匹配项进行计数,如果匹配项少于键值,则继续循环而不重新初始化 $loop_dic
。
我向您推荐一种 inverted 方法:不是逐行分解字符串,而是在检索值之前用空格替换换行符:您的字符串结构足够坚固,可以采用这种方法,并且你知道字段编号,所以这种方法应该有效。
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 ) );
}
我的 $values
和你的 $loop_dic
之间的主要区别在于,在 $values
主数组中你有列,但如果你更喜欢按行排列的数组,你可以轻松地转换它.
我用许多不同的“断线”测试了代码,并且它有效。我建议你用不同的字符串仔细测试它,看看它是否在任何情况下都能正常工作。
我需要从以这种方式格式化的文本文件中提取数据,使用 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
替换为 </code>(一个space)。我正在尝试将 @fusion3k 的工作代码与为解决此行为而精心设计的代码合并。文件结构可能是这样的:</p>
<pre><code>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
应该会生成类似于上面的工作代码,但考虑到存在不同的文本块分隔符,例如 semicolon (;
), 单引号 ('
) 或像在某些其他文件中一样,双引号 ("
),以分隔必须被认为是一个键的单个值,就像在这个数组中相对于上面的文本文件内容:
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 ) )
我处理了一个简单的字符串,以找到考虑 (分号) 和 (单引号 的 "working" 正则表达式] 或 双引号 )。现在我还没有找到使用所有三个定界符来定界文本块的文件,但似乎可以找到 semicolon+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>";
}
唯一有效的代码是最后一个 else,它使用了 @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 上是最新的),在页面加载完成之前......最好分块解析整个文件直到它的完整大小,也许,但如何去做?
非常感谢您的关注和帮助
要解决您的问题,常见的方法是对检索到的匹配项进行计数,如果匹配项少于键值,则继续循环而不重新初始化 $loop_dic
。
我向您推荐一种 inverted 方法:不是逐行分解字符串,而是在检索值之前用空格替换换行符:您的字符串结构足够坚固,可以采用这种方法,并且你知道字段编号,所以这种方法应该有效。
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 ) );
}
我的 $values
和你的 $loop_dic
之间的主要区别在于,在 $values
主数组中你有列,但如果你更喜欢按行排列的数组,你可以轻松地转换它.
我用许多不同的“断线”测试了代码,并且它有效。我建议你用不同的字符串仔细测试它,看看它是否在任何情况下都能正常工作。