سؤال

أحتاج إلى حفظ بعض المعلومات حول بعض الملفات. لا شيء يتوهم للغاية ، لذا اعتقدت أنني سأذهب مع سطر واحد بسيط لكل ملف نصي. شيء من هذا القبيل :

# write
io.print "%i %s %s\n" % [File.mtime(fname), fname, Digest::SHA1.file(fname).hexdigest]
# read
io.each do |line|
  mtime, name, hash = line.scanf "%i %s %s"
end

بالطبع هذا لا يعمل لأن اسم الملف يمكن أن يحتوي على مسافات (كسر SCANF) وكسر الأسطر (كسر IO#لكل منهما).

يمكن تجنب مشكلة كسر الخط عن طريق إسقاط استخدام كل منها والذهاب مع مجموعة من GETS ('')

while not io.eof?
  mtime = Time.at(io.gets(" ").to_i)
  name = io.gets " "
  hash = io.gets "\n"
end

التعامل مع المساحات في الأسماء مسألة أخرى. الآن نحن بحاجة إلى القيام ببعض الهروب.
ملاحظة: أحب الفضاء كحدد سجل ، لكنني لم أواجه أي مشكلة في تغييره لأسهل للاستخدام. في حالة أسماء الملفات ، فإن الشخص الوحيد الذي يمكن أن يساعد هو ASCII NUL " 0" ولكن لم يعد ملف محدد NUL هو ملف نصي حقًا ...

كان لدي في البداية جدارًا من النص الذي يوضح تكرارات كفاحي لجعل وظيفة هروب صحيحة وتبادلها ، لكنها كانت مملة وليست مفيدة حقًا. سأعطيك النتيجة النهائية فقط:

def write_name(io, val)
  io << val.gsub(/([\\ ])/, "\\\\\\1") # yes that' 6 backslashes !
end

def read_name(io)
  name, continued = "", true
  while continued
    continued = false
    name += io.gets(' ').gsub(/\\(.)/) do |c|
      if c=="\\\\"
        "\\"
      elsif c=="\\ "
        continued=true
        " "
      else
        raise "unexpected backslash escape  : %p (%s %i)" % [c, io.path, io.pos]
      end
    end
  end
  return name.chomp(' ')
end

لست سعيدًا على الإطلاق مع read_name. طريقة طويلة جدًا و akward ، أشعر أنه لا ينبغي أن يكون بهذه الصعوبة.

أثناء محاولة القيام بهذا العمل ، حاولت الخروج بطرق أخرى:

  • طريقة BitTorrent المشفرة / PHP Serialize: بادئة اسم الملف بطول الاسم ثم فقط io.read (name_len.to_i). إنه يعمل ولكنه pita حقيقي لتحرير الملف باليد. في هذه المرحلة ، نحن في منتصف الطريق إلى تنسيق ثنائي.

  • سلسلة#فحص: يبدو هذا واحد مصنوعًا صراحة لهذا الغرض! إلا أنه يبدو أن الطريقة الوحيدة لاستعادة القيمة هي من خلال Eval. أنا أكره فكرة تقييم سلسلة لم أقم بإنشائها من البيانات الموثوقة.

لذا. آراء؟ أليس هناك بعض lib التي يمكن أن تفعل كل هذا؟ هل أفتقد شيئًا واضحًا؟ كيف يمكنك أن تفعل ذلك ؟

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

المحلول

عندما تقول "حفظ" هل تقصد تخزين المعلومات في ملف؟

يمكنك استخدام وحدة CSV من مكتبة روبي القياسية. هذا يعني أن محددك هو فاصلة بدلاً من الفضاء ولكنه سيتعامل مع كل الهروب والتشجيع بالنسبة لك.

  • إذا كانت القيمة تحتوي على مسافات يتم إرفاقها "quotes"

  • إذا كانت القيمة تحتوي على علامات اقتباس ، فسيتم هروب حرف اقتباس كحرفتين اقتباس على سبيل المثال "hello" قد يصبح """hello"""

لكتابة التفاصيل إلى ملف:

require 'csv'

outfile = File.open('csvout', 'wb')
CSV::Writer.generate(outfile) do |csv|
  csv << [File.mtime(fname), fname, Digest::SHA1.file(fname).hexdigest]
end
outfile.close

لقراءتها مرة أخرى:

CSV::Reader.parse(File.open('csvout', 'rb')) do |row|
  p row
end

نصائح أخرى

CSV ، كما ذكرنا ، هو خيار جيد. آخر هو Yaml ("Yaml ليس لغة ترميز") ، والتي يمكن أن تتعامل مع بيانات أكثر تعسفية أكثر من CSV. هذه بعض البيانات:

require 'pp'
require 'yaml'

h = {
  :first_name => 'Fred',
  :last_name => 'Flinstone',
  :children => ['Bam Bam', 'Pebbles'],
  :exclamation => 'Yabba Dabba Doo',
}

دعنا نكتب البيانات إلى ملف بتنسيق YAML:

File.open('/tmp/foo.yaml', 'w') do |file|
  file.write h.to_yaml
end

الآن دعونا نرى كيف يبدو Yaml:

$ cat /tmp/foo.yaml
---
:exclamation: Yabba Dabba Doo
:first_name: Fred
:last_name: Flinstone
:children:
- Bam Bam
- Pebbles

وأخيراً ، دعنا نعيد إعادة تهيئة البيانات من ملف YAML:

pp YAML.load_file('/tmp/foo.yaml')
# => {:exclamation=>"Yabba Dabba Doo",
# =>  :first_name=>"Fred",
# =>  :last_name=>"Flinstone",
# =>  :children=>["Bam Bam", "Pebbles"]}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top