سؤال

لا أفهم رمز الياقوت هذا:

>> puts '\\ <- single backslash'
# \ <- single backslash

>> puts '\\ <- 2x a, because 2 backslashes get replaced'.sub(/\\/, 'aa')
# aa <- 2x a, because two backslashes get replaced

حتى الآن ، كل ذلك كما هو متوقع. ولكن إذا بحثنا عن 1 مع /\\/, واستبداله بـ 2 ، مشفرة بواسطة '\\\\', ، لماذا نحصل على هذا:

>> puts '\\ <- only 1 ... replace 1 with 2'.sub(/\\/, '\\\\')
# \ <- only 1 backslash, even though we replace 1 with 2

وبعد ذلك ، عندما نشفر 3 مع '\\\\\\', ، نحصل فقط على 2:

>> puts '\\ <- only 2 ... 1 with 3'.sub(/\\/, '\\\\\\')
# \\ <- 2 backslashes, even though we replace 1 with 3

أي شخص قادر على فهم سبب ابتلاع الانزلاق الخلفي في سلسلة الاستبدال؟ يحدث هذا في 1.8 و 1.9.

هل كانت مفيدة؟

المحلول

هذه مشكلة لأن backslash () بمثابة شخصية هروب لـ regexps والسلاسل. يمكنك استخدام المتغير الخاص & لتقليل العدد الخلفي للرقم في سلسلة استبدال GSUB.

foo.gsub(/\\/,'\&\&\&') #for some string foo replace each \ with \\\

تحرير: يجب أن أذكر أن قيمة & هي من مطابقة regexp ، في هذه الحالة تراجع خلفي واحد.

أيضا ، اعتقدت أن هناك طريقة خاصة لإنشاء سلسلة تعطيل شخصية الهروب ، ولكن على ما يبدو لا. لن ينتج أي من هؤلاء اثنين من المائل:

puts "\\"
puts '\\'
puts %q{\\}
puts %Q{\\}
puts """\\"""
puts '''\\'''
puts <<EOF
\\
EOF  

نصائح أخرى

جواب سريع

إذا كنت تريد تجنب كل هذا الالتباس ، استخدم بناء جملة كتلة أقل إرباكًا. فيما يلي مثال يحل محل كل سترات الذروة مع 2 -lashes:

"some\\path".gsub('\\') { '\\\\' }

تفاصيل بشعة

المشكلة هي ذلك عند استخدام subgsub) ، بدون كتلة ، يفسر روبي تسلسلات شخصية خاصة في المعلمة الاستبدال. للأسف، sub يستخدم الانزلاق الخلفي كحرف الهروب لهؤلاء:

\& (the entire regex)
\+ (the last group)
\` (pre-match string)
\' (post-match string)
\0 (same as \&)
\1 (first captured group)
\2 (second captured group)
\\ (a backslash)

مثل أي هروب ، وهذا يخلق مشكلة واضحة. إذا كنت تريد تضمين القيمة الحرفية لأحد التسلسلات المذكورة أعلاه (على سبيل المثال \1) في سلسلة الإخراج ، عليك الهروب منها. لذلك ، للحصول على Hello \1, ، تحتاج إلى أن تكون سلسلة الاستبدال Hello \\1. ولتمثيل هذا كسلسلة حرفية في روبي ، عليك الهروب "Hello \\\\1"

لذلك هناك تمريرين مختلفين هربين. الأول يأخذ السلسلة الحرفية وإنشاء قيمة السلسلة الداخلية. يأخذ الثاني قيمة السلسلة الداخلية ويحل محل التسلسلات أعلاه مع بيانات المطابقة.

إذا لم يتم تتبع ضربة خلفية من طابع يطابق أحد التسلسلات المذكورة أعلاه ، فسيتم مرور الذروة الخلفية (والشخصية التالية) عبر دون تغيير. هذا يؤثر أيضًا على اندلاع خلفي في نهاية السلسلة - سوف يمر عبر دون تغيير. من الأسهل رؤية هذا المنطق في كود روبينيوس ؛ فقط ابحث عن to_sub_replacement الطريقة في فئة السلسلة.

هنا بعض الأمثلة كيف String#sub يتم تحليل سلسلة الاستبدال:

  • 1 التراجع \ (التي لديها سلسلة حرفية "\\")

    يمر عبر دون تغيير لأن الانزلاق الخلفي في نهاية السلسلة وليس له أحرف بعد ذلك.

    نتيجة: \

  • 2 التراجع \\ (التي لها سلسلة حرفية "\\\\")

    يتطابق زوج من الخلفات المتسابقة مع تسلسل الذروة المتخلفة (انظر \\ أعلاه) ويتم تحويلها إلى عصر خلفي واحد.

    نتيجة: \

  • 3 عوامل التراجع \\\ (التي لها سلسلة حرفية "\\\\\\")

    أول اثنين من الخلايا الخلفية تطابق \\ تسلسل وتحويلها إلى عصر خلفي واحد. ثم يكون التراجع الخلفي النهائي في نهاية السلسلة بحيث يمر عبر دون تغيير.

    نتيجة: \\

  • 4 خلفية \\\\ (التي لها سلسلة حرفية "\\\\\\\\")

    اثنين من أزواج من الخلايا المتراكمة كل تطابق \\ تسلسل وتحويلها إلى عصر خلفي واحد.

    نتيجة: \\

  • 2 backslashes مع شخصية في الوسط \a\ (التي لها سلسلة حرفية "\\a\\")

    ال \a لا يتطابق مع أي من تسلسل الهروب بحيث يُسمح له بالمرور عبر دون تغيير. يُسمح أيضًا بتراجع الخلاف.

    نتيجة: \a\

    ملحوظة: يمكن الحصول على نفس النتيجة من: \\a\\ (مع السلسلة الحرفية: "\\\\a\\\\")

بعد فوات الأوان ، كان من الممكن أن يكون هذا أقل إرباكًا إذا String#sub استخدمت شخصية هروب مختلفة. ثم لن تكون هناك حاجة إلى الهروب من كل ما تبقى.

أرغ ، مباشرة بعد أن كتبت كل هذا ، أدركت ذلك \ يستخدم للإشارة إلى المجموعات في سلسلة الاستبدال. أعتقد أن هذا يعني أنك تحتاج إلى حرفي \\ في سلسلة الاستبدال لاستبدال واحد \. للحصول على حرفي \\ تحتاج أربعة \S ، لاستبدال واحد مع اثنين تحتاج فعلا ثمانية (!).

# Double every occurrence of \. There's eight backslashes on the right there!
>> puts '\\'.sub(/\\/, '\\\\\\\\')

أي شيء أفتقده؟ أي طرق أكثر كفاءة؟

مسح القليل من الارتباك على السطر الثاني من الكود للمؤلف.

أنت قلت:

>> puts '\\ <- 2x a, because 2 backslashes get replaced'.sub(/\\/, 'aa')
# aa <- 2x a, because two backslashes get replaced

2 لا يتم استبدال الانزلاقات الخلفية هنا. أنت تحل محل 1 هرب تراجع مع اثنين A ('aa'). هذا هو ، إذا كنت تستخدم .sub(/\\/, 'a'), سترى واحدة فقط "أ"

'\\'.sub(/\\/, 'anything') #=> anything

يذكر كتاب Pickaxe هذه المشكلة الدقيقة ، في الواقع. إليك بديل آخر (من الصفحة 130 من أحدث إصدار)

str = 'a\b\c'               # => "a\b\c"
str.gsub(/\\/) { '\\\\' }   # => "a\\b\\c"
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top