|
|
@@ -190,11 +190,14 @@ class ExamPdfController extends Controller
|
|
|
{
|
|
|
$options = [];
|
|
|
|
|
|
+ // 【修复】先移除SVG内容,避免误匹配SVG注释中的 BD:DC、A:B 等内容
|
|
|
+ $contentWithoutSvg = preg_replace('/<svg[^>]*>.*?<\/svg>/is', '[SVG_PLACEHOLDER]', $content);
|
|
|
+
|
|
|
// 1. 尝试匹配多种格式的选项:A. / A、/ A: / A.(中文句点)/ A.(无空格)
|
|
|
- // 支持格式:A.-1 / A. -1 / A、-1 / A:-1
|
|
|
- $pattern = '/([A-D])[\.、:.:]\s*(.+?)(?=\s*[A-D][\.、:.:]|$)/su';
|
|
|
+ // 【修复】选项标记必须在行首或空白后,避免误匹配 SVG 注释中的 BD:DC 等内容
|
|
|
+ $pattern = '/(?:^|\s)([A-D])[\.、:.:]\s*(.+?)(?=(?:^|\s)[A-D][\.、:.:]|$)/su';
|
|
|
|
|
|
- if (preg_match_all($pattern, $content, $matches, PREG_SET_ORDER)) {
|
|
|
+ if (preg_match_all($pattern, $contentWithoutSvg, $matches, PREG_SET_ORDER)) {
|
|
|
foreach ($matches as $match) {
|
|
|
$optionText = trim($match[2]);
|
|
|
// 移除末尾的换行和空白
|
|
|
@@ -210,9 +213,10 @@ class ExamPdfController extends Controller
|
|
|
|
|
|
// 2. 如果上面没提取到,尝试按换行分割
|
|
|
if (empty($options)) {
|
|
|
- $lines = preg_split('/[\r\n]+/', $content);
|
|
|
+ $lines = preg_split('/[\r\n]+/', $contentWithoutSvg);
|
|
|
foreach ($lines as $line) {
|
|
|
$line = trim($line);
|
|
|
+ // 【修复】行首匹配选项标记
|
|
|
if (preg_match('/^([A-D])[\.、:.:]\s*(.+)$/u', $line, $match)) {
|
|
|
$optionText = trim($match[2]);
|
|
|
if (!empty($optionText)) {
|
|
|
@@ -236,8 +240,11 @@ class ExamPdfController extends Controller
|
|
|
*/
|
|
|
private function separateStemAndOptions(string $content): array
|
|
|
{
|
|
|
- // 检测是否有选项(支持多种格式)
|
|
|
- $hasOptions = preg_match('/[A-D][\.、:.:]/u', $content);
|
|
|
+ // 【修复】先移除SVG内容,避免误匹配SVG注释中的 BD:DC、A:B 等内容
|
|
|
+ $contentWithoutSvg = preg_replace('/<svg[^>]*>.*?<\/svg>/is', '[SVG_PLACEHOLDER]', $content);
|
|
|
+
|
|
|
+ // 【修复】检测是否有选项时,要求选项标记在行首或空白后
|
|
|
+ $hasOptions = preg_match('/(?:^|\s)[A-D][\.、:.:]/u', $contentWithoutSvg);
|
|
|
|
|
|
if (!$hasOptions) {
|
|
|
return [$content, []];
|
|
|
@@ -248,17 +255,36 @@ class ExamPdfController extends Controller
|
|
|
|
|
|
// 如果提取到选项,分离题干
|
|
|
if (!empty($options)) {
|
|
|
- // 找到第一个选项的位置,之前的内容是题干
|
|
|
- if (preg_match('/^(.+?)(?=[A-D][\.、:.:])/su', $content, $match)) {
|
|
|
+ // 【修复】找到第一个选项的位置,要求选项标记在行首或空白后
|
|
|
+ if (preg_match('/^(.+?)(?=(?:^|\s)[A-D][\.、:.:])/su', $contentWithoutSvg, $match)) {
|
|
|
$stem = trim($match[1]);
|
|
|
+ // 如果题干中有SVG占位符,从原始内容中提取对应部分
|
|
|
+ if (strpos($stem, '[SVG_PLACEHOLDER]') !== false) {
|
|
|
+ // 找到原始内容中对应位置的题干
|
|
|
+ $stemLength = mb_strlen(str_replace('[SVG_PLACEHOLDER]', '', $stem));
|
|
|
+ // 使用更精确的方法:找到第一个有效选项标记的位置
|
|
|
+ foreach (['A.', 'A、', 'A:', 'A.', 'A:'] as $marker) {
|
|
|
+ // 只匹配在空白后的选项标记
|
|
|
+ if (preg_match('/\s' . preg_quote($marker, '/') . '/', $content, $m, PREG_OFFSET_CAPTURE)) {
|
|
|
+ $pos = $m[0][1];
|
|
|
+ if ($pos > 0) {
|
|
|
+ $stem = trim(mb_substr($content, 0, $pos));
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
} else {
|
|
|
// 如果正则失败,尝试按位置分割
|
|
|
$stem = $content;
|
|
|
foreach (['A.', 'A、', 'A:', 'A.', 'A:'] as $marker) {
|
|
|
- $pos = mb_strpos($content, $marker);
|
|
|
- if ($pos !== false && $pos > 0) {
|
|
|
- $stem = trim(mb_substr($content, 0, $pos));
|
|
|
- break;
|
|
|
+ // 【修复】只匹配在空白后的选项标记
|
|
|
+ if (preg_match('/\s' . preg_quote($marker, '/') . '/', $content, $m, PREG_OFFSET_CAPTURE)) {
|
|
|
+ $pos = $m[0][1];
|
|
|
+ if ($pos > 0) {
|
|
|
+ $stem = trim(mb_substr($content, 0, $pos));
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|