Go এর জন্য CSV বয়লারপ্লেট · Felix Geisendörfer

Go এর জন্য CSV বয়লারপ্লেট · Felix Geisendörfer


Go এর জন্য CSV বয়লারপ্লেট · Felix Geisendörfer

প্রকাশিত:

tl; dr: Go-তে CSV ফাইল পড়ার এবং লেখার জন্য এখানে আমার বয়লারপ্লেট। আমি আশা করি আপনি এটি দরকারী পাবেন.

প্রতি একবারে, আমাকে Go-তে CSV ফাইলগুলি পড়তে এবং/বা লিখতে হবে। তত্ত্বে এটা সহজ। একজনকে শুধুমাত্র স্ট্যান্ডার্ড লাইব্রেরি থেকে এনকোডিং/সিএসভি অন্তর্ভুক্ত করতে হবে এবং একটি গো স্ট্রাকট এবং CSV রেকর্ডগুলির মধ্যে ম্যাপ করতে কিছুটা বয়লারপ্লেট লিখতে হবে।

যাইহোক, ফলস্বরূপ কোডটি প্রায়ই বিভিন্ন কারণে আমাকে অসন্তুষ্ট করে রেখেছে:

  1. আমি চাকাটি পুনরায় উদ্ভাবন করতে অনেক বেশি সময় ব্যয় করি, কোড গঠনের বিভিন্ন উপায় নিয়ে খেলা করি।
  2. কোডটি বজায় রাখা বিরক্তিকর, যেমন আউটপুট কলামের ক্রম পরিবর্তন করার জন্য প্রায়শই কমপক্ষে 4টি জায়গায় পরিবর্তনের প্রয়োজন হয়: কাঠামোর সংজ্ঞা, শিরোনাম লেখা, রেকর্ড লেখা, এবং রেকর্ড পড়া।
  3. খারাপ CSV ইনপুটের ক্ষেত্রে ফলাফলটি খুব শক্তিশালী নয় এবং অনুপস্থিত, অপ্রত্যাশিত বা ভুল কলাম বা হেডারের মতো সমস্যাগুলি পরিচালনা করে না।
  4. ত্রুটি বার্তাগুলি প্রায়শই সারি নম্বর, কলামের নাম ইত্যাদির মতো গুরুত্বপূর্ণ তথ্য হারিয়ে ফেলে।
  5. আমি কীভাবে এর জন্য ভাল পরীক্ষা লিখব যা ভবিষ্যতে বজায় রাখা সহজ হবে?

এটি মোকাবেলা করার একটি উপায় হল একটি উচ্চ স্তরের লাইব্রেরি ব্যবহার করা, এবং আপনার অবশ্যই এটি বিবেচনা করা উচিত। কিন্তু বাস্তবতা প্রায়ই একটি কুৎসিত জগাখিচুড়ি. আপনি হয়তো দেখতে পাচ্ছেন যে আপনার পছন্দের লাইব্রেরি একটি নির্দিষ্ট প্রান্তের ক্ষেত্রে পরিচালনা করে না, যেমন একাধিক হেডার সারি উপস্থিত থাকা (আমার ব্যাঙ্ক 6টি হেডার লাইন রাখতে পছন্দ করে)। অথবা আপনি যে ত্রুটি বার্তাগুলি পাচ্ছেন তা আপনার ব্যবহারকারীদের জন্য যথেষ্ট সহায়ক নয়৷ অথবা ত্রুটি হ্যান্ডলিং খুব কঠোর. অথবা প্রতিফলন ভিত্তিক ম্যাপিং খুব জাদু. অথবা কর্মক্ষমতা sucks. আমি চালিয়ে যেতে পারি, কিন্তু আপনি বুঝতে পেরেছেন – CSV এর সাথে ডিল করা বাজে এবং এটিকে একটি বিন্যাস বলা একটি বিন্যাসের ধারণার জন্য কিছুটা অপমানজনক। আমার দৃষ্টিকোণ থেকে, এই সমস্যাটির জন্য একটি লাইব্রেরি লেখা একটি ভয়ঙ্করভাবে অতিরিক্ত প্রকৌশলী জগাখিচুড়ি তৈরি না করে বা অনেক প্রান্তের ক্ষেত্রে উপেক্ষা করা ছাড়াই সম্ভব নয়।

তাই শেষ পর্যন্ত মনে হচ্ছে আপনাকে দুটি সাবঅপ্টিমাল বিকল্পের মধ্যে বেছে নিতে হবে। আপনি একগুচ্ছ লাইব্রেরি ব্যবহার করে দেখতে পারেন এবং আপনার প্রয়োজনের সবচেয়ে কাছের একটি ব্যবহার এবং/অথবা কাঁটাচামচ করতে পারেন। অথবা আপনি আপনার নিজের বয়লারপ্লেটটি মিলিয়নতম বার রোল করতে পারেন, শুধুমাত্র কিছুক্ষণ পরেই ভয়ঙ্কর বিরক্তির সাথে এটির দিকে ফিরে তাকাতে পারেন।

আপনার পছন্দ সম্ভবত আপনার ইনপুট এর পাগলামি উপর নির্ভর করা উচিত, এবং আমি আপনার জন্য এটি করতে সক্ষম হবে না. কিন্তু আমি সিদ্ধান্ত নিয়েছি যে আমি ভবিষ্যতে আমার জন্য বয়লারপ্লেট রুটটিকে কিছুটা কম বিরক্তিকর করতে চাই। এটি সম্পন্ন করার জন্য আমি একটি বয়লারপ্লেট টেমপ্লেট তৈরি করেছি যা আমি আমার ভবিষ্যতের প্রয়োজনের জন্য অনুলিপি এবং পেস্ট করব, প্রয়োজন অনুসারে এটি সামঞ্জস্য করব। ব্লগ পোস্টের বাকি অংশের জন্য আমি এটি ডিজাইন করার জন্য আমার চিন্তা প্রক্রিয়ার মাধ্যমে আপনাকে হাঁটতে চাই।

শুরু করার জন্য, আমি আমার উদাহরণ হিসাবে বাস্তব জগতের CSV ব্যবহার করার সিদ্ধান্ত নিয়েছি, যার মধ্যে সাধারণ কুশ্রীতার বিট রয়েছে যা একজনকে মোকাবেলা করতে হয়। আমি দ্রুত cars.csv নামে একটি ফাইল খুঁজে পেয়েছি যা দেখতে এইরকম:

Car;MPG;Cylinders;Displacement;Horsepower;Weight;Acceleration;Model;Origin
STRING;DOUBLE;INT;DOUBLE;DOUBLE;DOUBLE;DOUBLE;INT;CAT
Chevrolet Chevelle Malibu;18.0;8;307.0;130.0;3504.;12.0;70;US
Buick Skylark 320;15.0;8;350.0;165.0;3693.;11.5;70;US
Plymouth Satellite;18.0;8;318.0;150.0;3436.;11.0;70;US
...

আপনি দেখতে পাচ্ছেন, এখানে ইতিমধ্যে দুটি কুৎসিত বিট পরীক্ষা করা হয়েছে: একটি বিভাজক হিসাবে কমার পরিবর্তে সেমিকোলন, এবং একটি দ্বিতীয় শিরোনাম লাইন যা যেকোনো কারণেই টাইপ তথ্য ধারণ করে বলে মনে হয়। দুর্দান্ত নয়, ভয়ানক নয়, তবে সেখানে যা আছে তার জন্য বেশ সাধারণ।

আমার বয়লারপ্লেটের লক্ষ্য হল ফাইল ফরম্যাটটি এমনভাবে পড়া এবং লেখা যা শুরুতে বর্ণিত পাঁচটি সমস্যার সমাধান করে। এটি একটি অত্যধিক পরিমাণ বিমূর্ততা ছাড়া তা করা উচিত, এবং অবশ্যই কোন প্রতিফলন, তবুও যতটা সম্ভব মার্জিতভাবে। নীচে দেখানো হিসাবে মেমরিতে ডেটা ধরে রাখার জন্য একটি মধ্যবর্তী গো স্ট্রাকটও থাকা উচিত।

type Car struct {
	Car          string
	MPG          float64
	Cylinders    int64
	Displacement float64
	Horsepower   float64
	Weight       float64
	Acceleration float64
	Model        int64
	Origin       string
}

সাধারণত আমার প্রথম নিরীহ প্রচেষ্টা হল কিছু কোড লেখা যা হেডার এবং স্ট্রাকট ম্যাপিংকে এমনভাবে সংজ্ঞায়িত করে যা CSV এনকোডিং/ডিকোডিং থেকে আলাদা। এখানে একটি উদাহরণ:

var carHeaders = ()string{
  "Car",
  "MPG",
  "Cylinders",
  // remaining columns ...
}

func (c *Car) UnmarshalRecord(record ()string) error {
	if got, want := len(record), len(carHeaders); got != want {
		return fmt.Errorf("bad column number: got=%d want=%d", got, want)
	}
	c.Car = record(0)
	if c.MPG, err = strconv.ParseFloat(record(1), 64); err != nil {
		return fmt.Errorf("column=%q: %w", carHeaders(1), err)
	} else if c.Cylinders, err = strconv.ParseInt(record(2), 10, 64); err != nil {
		return fmt.Errorf("column=%q: %w", carHeaders(2), err)
	}
	// remaining columns ...
	return nil
}

func (c *Car) MarshalRecord() (()string, error) {
	return ()string{
		c.Car,
		fmt.Sprintf("%f", c.MPG),
		fmt.Sprintf("%d", c.Cylinders),
		// remaining columns ...
	}, nil
}

এটি কাজ করে, কিন্তু যেকোনও কলাম যোগ করা, অপসারণ করা বা পুনর্বিন্যাস করার জন্য আমাদের 4 থেকে 5টি ভিন্ন জায়গায় আমাদের কোড পরিবর্তন করতে হবে এবং আমি শুরুতে উল্লেখ করেছি, আমরা এটি এড়াতে চাই। সুতরাং আসুন কিছু বিমূর্ততা চেষ্টা করি যা আমাদের কলামগুলিকে ঘোষণামূলকভাবে নির্দিষ্ট করতে দেয় এবং কীভাবে সেগুলি মার্শাল/আনমার্শাল করতে হয়:

type carColumn struct {
	Name           string
	Type           string
	UnmarshalValue func(*Car, string) error
	MarshalValue   func(*Car) (string, error)
}

var carColumns = ()carColumn{
	{
		"Car",
		"STRING",
		func(c *Car, val string) error {
			c.Car = val
			return nil
		},
		func(c *Car) (string, error) {
			return c.Car, nil
		},
	},
	{
		"MPG",
		"DOUBLE",
		func(c *Car, val string) (err error) {
			c.MPG, err = strconv.ParseFloat(val, 64)
			return
		},
		func(c *Car) (string, error) {
			return fmt.Sprintf("%f", c.MPG), nil
		},
	},
	{
		"Cylinders",
		"INT",
		func(c *Car, val string) (err error) {
			c.Cylinders, err = strconv.ParseInt(val, 10, 64)
			return
		},
		func(c *Car) (string, error) {
			return fmt.Sprintf("%d", c.Cylinders), nil
		},
	},
	// remaining columns ...
}

এটি অবশ্যই একটি বিট শব্দপূর্ণ, কিন্তু এটি আমাদের সমস্যার সমাধান করে। আমাদের কলামগুলিকে পুনরায় সাজানো এখন একটি একক কাট এবং পেস্ট অপারেশন, এবং একটি কলাম যোগ করা বা সরানোও অনেক সহজ হয়েছে৷ এই পদ্ধতির এমনকি আমাদের একটি সুবিধাজনক উপায় দেয় Type CSV ফাইলের দ্বিতীয় লাইনে তথ্য পাওয়া যায়, তাই পরবর্তীতে আমাদের নিজস্ব CSV ফাইল লেখার সময় আমরা সহজেই এই ব্যঙ্গের নকল করতে পারি।

আমরা এখনও আমাদের আপডেট করতে হবে UnmarshalRecord এবং MarshalRecord আগে থেকে ভাল খবর হল যে আমাদের এই কোডটি আবার সংশোধন করতে হবে এমন সম্ভাবনা নেই:

func (c *Car) UnmarshalRecord(record ()string) error {
	if got, want := len(record), len(carColumns); got != want {
		return fmt.Errorf("bad number of columns: got=%d want=%d", got, want)
	}
	for i, col := range carColumns {
		if err := col.UnmarshalValue(c, record(i)); err != nil {
			return fmt.Errorf("column=%q: %w", col.Name, err)
		}
	}
	return nil
}

func (c *Car) MarshalRecord() (()string, error) {
	record := make(()string, len(carColumns))
	for i, col := range carColumns {
		val, err := col.MarshalValue(c)
		if err != nil {
			return nil, err
		}
		record(i) = val
	}
	return record, nil
}

এখন CSV ডিকোডিং নিজেই বাস্তবায়ন করার সময়। আমার বয়লারপ্লেট এর মাধ্যমে এটি প্রয়োগ করে ReadCarsCSV ফাংশন যা নীচে দেখা যাবে।

const carComma = ';'

func ReadCarsCSV(r io.Reader) (()*Car, error) {
	cr := csv.NewReader(r)
	cr.Comma = carComma

	records, err := cr.ReadAll()
	if err != nil {
		return nil, err
	}

	cars := make(()*Car, 0, len(records))
	for i, record := range records {
		if got, want := len(record), len(carColumns); got != want {
			return nil, fmt.Errorf("row=%d: bad number of columns: got=%d want=%d", i+1, got, want)
		}

		switch i {
		case 0:
			for i, got := range record {
				if want := carColumns(i).Name; got != want {
					return nil, fmt.Errorf("unexpected header column %d: got=%q want=%q", i, got, want)
				}
			}
		case 1:
			for i, got := range record {
				if want := carColumns(i).Type; got != want {
					return nil, fmt.Errorf("unexpected type column %d: got=%q want=%q", i, got, want)
				}
			}
		default:
			car := &Car{}
			if err := car.UnmarshalRecord(record); err != nil {
				return nil, fmt.Errorf("row=%d: %w", i+1, err)
			}
			cars = append(cars, car)
		}
	}

	return cars, nil
}

উপরের কোডটি প্রদত্ত থেকে পড়া সমস্ত গাড়ি ফেরত দেওয়ার প্রধান কাজটি সম্পন্ন করে io.Readerকিন্তু এটি কলামের সংখ্যা, সেইসাথে প্রথম দুটি সারিতে পাওয়া হেডার এবং টাইপ নামগুলিকেও যাচাই করে। কোনো একটি রেকর্ডে কিছু ভুল হলে সারি নম্বরের পাশাপাশি আপত্তিকর কলামের নাম উভয় সহ ত্রুটি বার্তাগুলিও ভাল হওয়া উচিত।

এটি পরিবর্তন করাও সহজ হওয়া উচিত। উদাহরণস্বরূপ, যদি আপনি পড়ার সময় দ্বিতীয় শিরোনাম লাইনটি উপেক্ষা করতে চান তবে কোডটি সরিয়ে ফেলুন। অথবা সমস্ত শিরোনামগুলির একটি নির্দিষ্ট অবস্থানের প্রয়োজনের পরিবর্তে, আপনি ইনপুট থেকে তাদের নামগুলির সাথে নামের সাথে মিল করে গতিশীলভাবে তাদের অবস্থান আবিষ্কার করতে পারেন carColumns টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো করে রেকর্ড করুন

অথবা আপনি সিদ্ধান্ত নিতে পারেন যে আপনার মেমরির ব্যবহার এবং GC চাপ কমাতে একটি স্ট্রিমিং ইন্টারফেস প্রয়োজন:

type CarReader struct {
	r io.Reader
	// ...
}

func (r *CarReader) Read(c *Car) error {
	// ...
}

সম্ভাবনাগুলি অন্তহীন, এবং আপনি আপনার লাইব্রেরির পছন্দ দ্বারা নিজেকে এক কোণে ব্যাক পাবেন না।

পরবর্তীতে আমাদের ডেটাকে আবার CSV-এ রূপান্তর করা হচ্ছে। আমার সমাধান এই মত দেখায়:

func WriteCarsCSV(w io.Writer, cars ()*Car) error {
	cw := csv.NewWriter(w)
	cw.Comma = carComma

	header := make(()string, len(carColumns))
	types := make(()string, len(carColumns))
	for i, col := range carColumns {
		header(i) = col.Name
		types(i) = col.Type
	}
	cw.Write(header)
	cw.Write(types)

	for _, car := range cars {
		record, err := car.MarshalRecord()
		if err != nil {
			return err
		}
		cw.Write(record)
	}
	cw.Flush()
	return cw.Error()
}

আপনি দেখতে পাচ্ছেন, আমরা অদ্ভুত দ্বিতীয় হেডার লাইনটি লেখার যত্ন নিই। আমরা প্রত্যেকের জন্য ত্রুটি পরীক্ষা না করে কিছু কোড সংরক্ষণ করি cw.Write() কল করুন এবং ফিরে এসে তাদের পরিচালনা করুন cw.Error() পরিবর্তে শেষে.

এখন আমাদের পরীক্ষার কথা ভাবা উচিত। পরীক্ষার ক্ষেত্রে আমি 80/20 নিয়মের একজন বড় অনুরাগী, তাই নীচে একটি পরীক্ষা রয়েছে যা খুব দক্ষতার সাথে সুখী পথকে কভার করে।

func TestCSVReadWriteCycle(t *testing.T) {
	in := strings.TrimSpace(`
Car;MPG;Cylinders;Displacement;Horsepower;Weight;Acceleration;Model;Origin
STRING;DOUBLE;INT;DOUBLE;DOUBLE;DOUBLE;DOUBLE;INT;CAT
Chevrolet Chevelle Malibu;18.0;8;307.0;130.0;3504.;12.0;70;US
Buick Skylark 320;15.0;8;350.0;165.0;3693.;11.5;70;US
Plymouth Satellite;18.0;8;318.0;150.0;3436.;11.0;70;US
`)
	wantOut := strings.TrimSpace(`
Car;MPG;Cylinders;Displacement;Horsepower;Weight;Acceleration;Model;Origin
STRING;DOUBLE;INT;DOUBLE;DOUBLE;DOUBLE;DOUBLE;INT;CAT
Chevrolet Chevelle Malibu;18.000000;8;307.000000;130.000000;3504.000000;12.000000;70;US
Buick Skylark 320;15.000000;8;350.000000;165.000000;3693.000000;11.500000;70;US
Plymouth Satellite;18.000000;8;318.000000;150.000000;3436.000000;11.000000;70;US
`)

	for i := 0; i < 2; i++ {
		cars, err := ReadCarsCSV(strings.NewReader(in))
		if err != nil {
			t.Fatal(err)
		}

		buf := &bytes.Buffer{}
		if err := WriteCarsCSV(buf, cars); err != nil {
			t.Fatal(err)
		}
		gotOut := strings.TrimSpace(buf.String())
		if gotOut != wantOut {
			t.Fatalf("\ngot:\n%s\nwant:\n%s", gotOut, wantOut)
		}
		in = gotOut
	}
}

পরীক্ষাটি প্রথমে প্রদান করা হয় in CSV, এবং নিশ্চিত করে যে এটিকে আবার CSV-এ রূপান্তরিত করা হয় wantOut যা একটু ভিন্ন কারণ Go আমাদের ফ্লোটিং পয়েন্ট নম্বরগুলিকে ফর্ম্যাট করে। তারপরে পরীক্ষাটি প্রথম পুনরাবৃত্তি থেকে আউটপুটকে দ্বিতীয়টির জন্য ইনপুট হিসাবে বিবেচনা করে। এটি নিশ্চিত করে যে আমাদের বাস্তবায়ন মূল বিন্যাস পড়তে পারে, সেইসাথে এটি নিজেই তৈরি করা সামান্য ভিন্ন আউটপুট।

অবশ্যই আমরা মূল ফাইল থেকে ফ্লোট ফরম্যাটিং কুইর্কগুলি পুনরুত্পাদন করার লক্ষ্য রাখতে পারি। প্রথম নজরে দেখে মনে হচ্ছে সমস্ত ফ্লোটগুলি পিরিয়ডের পরে এক অঙ্ক দিয়ে ফর্ম্যাট করা হয়েছে৷ যাইহোক, একটি ঘনিষ্ঠ চেহারা প্রকাশ করে যে Weight কলামের একটি পিরিয়ড আছে, কিন্তু নিচের অঙ্কটি এড়িয়ে যায়, যেমন 3504.. আমি এই খরগোশের গর্তে না যাওয়ার সিদ্ধান্ত নিয়েছি, কিন্তু এটা পরিষ্কার হওয়া উচিত যে আমাদের বয়লারপ্লেট পদ্ধতি আমাদেরকে এইরকম CSV ব্যঙ্গগুলির সাথে মোকাবিলা করার জন্য একটি দুর্দান্ত অবস্থানে রাখে৷

আপনি CSV পার্স করার বিষয়ে কতটা গুরুতর তার উপর নির্ভর করে, আপনি সম্ভবত কয়েকটি ত্রুটির ক্ষেত্রেও পরীক্ষা করতে চান এবং হয়ত এতে কিছু অস্পষ্টতাও ফেলতে চান। কিন্তু আমি সিদ্ধান্ত নিয়েছি যে উপরের পরীক্ষাটি আমার বয়লারপ্লেটের জন্য যথেষ্ট ভালো, তাই আপনাকে এই অংশটি নিজেই করতে হবে।

ঠিক আছে, এটাই। আমি আপনার চিন্তা শুনতে চাই, বিশেষ করে যদি আপনার কাছে এটি আরও উন্নত করার জন্য ধারণা থাকে। অথবা আপনি যদি একগুচ্ছ বয়লারপ্লেট কপি ও পেস্ট করার ধারণার ভালো বিকল্প দেখতে পান তাহলে আমাকে জানান। আমি এটি পছন্দ করি এমনটা নয়, তবে যতদূর আমি বলতে পারি এটি এই ক্ষেত্রে গো-এর সাথে কাজ করার জন্য একটি মিষ্টি জায়গা হিট করে।

ধন্যবাদ থর্স্টেন বল এবং টমাস সেনার্ট এই পোস্টটি পর্যালোচনা করার জন্য এবং অনেক মূল্যবান পরামর্শ দেওয়ার জন্য।

— ফেলিক্স গেইসেনডোরফার


আরএসএস বা ই-মেইলের মাধ্যমে এই ব্লগে সদস্যতা নিন বা এর মাধ্যমে আমার কাছ থেকে ছোট আপডেট পান টুইটার.





Source link

Postagens Similares

  • ongeza Whisper kwenye Kitufe chako cha Kitendo cha iOS

    IPhone mpya ziliacha kitufe cha kunyamazisha halisi na kitufe cha vitendo kilichowezeshwa na programu, ambacho huruhusu ubinafsishaji fulani. kiwango cha juu zaidi cha ubinafsishaji ni Njia za mkato. Unukuzi chaguomsingi wa Apple ni mbaya. Nilipata iPhone mpya hivi majuzi na kwa hivyo hii ndiyo nafasi yangu. Ilinichukua muda kujua jinsi ya kutengeneza njia ya mkato…

  • Podcast Baru Favorit Saya tahun 2023

    Sebagai seseorang yang banyak belajar melalui podcast, saya telah membuat daftar pilihan selama 4 tahun berturut-turut (lihat daftar utama tahun 2019, lalu perbedaan tahun 2020, 2021, dan 2022), jadi inilah waktunya untuk melakukan tahun ke-5(!) Pembaruan 2024: ini reset 2024 Pada tahun 2023 saya akhirnya menjadi pembawa acara podcast semi-sukses, setelah upaya 1, 2, 3….

  • 飛行機への恐怖 – Kev Quirk

    2026 年 5 月 1 日 私は最近ネイサン・ミルワード著『ロング・ライド・ホーム』を読んでいたのですが、その本の中で彼は飛行機に乗らなければならないことと飛行機への恐怖について次のように語っています。 これは私が避けたかったこと(飛行機に乗るということ)であり、私の飛行機への恐怖は、上空での制御の欠如から生じたものである(と私は思います)。 すべては他人の手に委ねられています。何も悪いことが起こらないことを祈りながら、ただそこに座っていてください。なぜなら、もしそうなったとしても、人生でやるべきこと、やるべきことのすべてを考えている自由落下の瞬間より悪いことは考えられません。物事を正したり、間違いから学ぶにはもう手遅れだからです。あなたの時代がやって来ました、そして今はもう終わりです。これは飛ぶことと同じくらい、後悔することへの恐怖なのだと思いますが。 — ネイサン・ミルワード これ 本当に 仕事で半定期的に飛行機に乗る人にとって、飛行機が怖いと話すと、人々はよく驚かれます。 分からない、多分 恐れ 強すぎる言葉ですが、確かにとても不快な気持ちになります。特に乱気流がある場合。 ネイサンと同じように、それはコントロールの喪失だと思います。はい、そうです、私はバイクに乗っているときや自動車事故で怪我をする可能性がはるかに高いです。しかし、違うのは、車や自転車で事故に遭ったとしても、私はある程度コントロールできており、(特に車の場合)軽い怪我だけで済んでしまう可能性がかなりあるということです。 一方、飛行機事故に遭った場合、私は とても 可能な限り最も恐ろしい方法で死ぬ可能性が高く、それは私を完全に恐怖させます。私の旅行の多くが大西洋を横断するため、広大な水域を越えることが、この状況をさらに悪化させることがよくあります。素晴らしい。 それを克服するには? 神は私が試したことを知っています!私はブリティッシュ・エアウェイズで自信を持って飛ぶコースを受講したことがあり、知識は増えましたが、不安はあまり解消されませんでした。 睡眠薬を試したこともありますが、イギリスで市販されているものはどれもクソで、私にはまったく効果がありません。眠くなることもありません。何人かの人が鎮静剤を勧めましたが、それは私には不快です。それは違法であるだけでなく、彼らが私に何をするのかわかりません。結構です。 私は不安を抱えた飛行家になる運命にあると思うので、ただそれに対処しなければなりません。 数週間後にまたアメリカに行く予定ですが、いつものように不安が私の腹の中で湧き上がり始めています。 ヒントをお持ちの方がいらっしゃいましたら、ぜひお聞かせください。 意見 メールで返信 もっと購読してください! 私の最新のワッフルを読むために何度もここに来る必要はありません。新しいコンテンツを公開するたびに更新情報を受信できるように購読する方法が 2 つあります。 Source link

  • মনোযোগের মাধ্যমে আপনার জীবনকাল প্রসারিত করা

    আমি কখনও শিখেছি সবচেয়ে আশ্চর্যজনক জিনিস এক যে অভিনবত্ব এবং মনোযোগ আপনার জীবনকাল প্রসারিত. অথবা, আরো সঠিকভাবে, মনোযোগ এবং অভিনবত্ব ধীর সময়. এবং বিপরীতটিও সত্য: প্যাটার্ন এবং বিভ্রান্তি এটিকে ত্বরান্বিত করে। আমি এই ধাঁধার উত্তরের জন্য অনেক দিন ধরে অনুসন্ধান করেছি। এই মডেলটি ব্যাখ্যা করে যে আমরা যখন শিশু ছিলাম তখন কেন সময় এত ধীর…

  • 獎勵應用程式 Freecash 是如何透過詐騙登上應用程式商店榜首的

    一款名為 Freecash 的資料收集應用程式似乎欺騙了用戶,因為它迅速上升到 App Store 和 Google Play 的排行榜前列,並在該排行榜上停留了數月,直到最近被禁止。 如果您今年使用過 TikTok,您很可能遇到過 Freecash 的廣告。該應用程式被宣傳為一種只需滾動 TikTok 就能賺錢的方式,並在近幾個月躍居應用程式商店榜首,在美國應用程式商店中排名第二。 事實上,根據網路安全公司 Malwarebytes 稱,Freecash 向用戶付費玩手機遊戲,同時收集大量敏感資料。 Malwarebytes 的一份報告指出,該應用程式可能會收集有關用戶種族、宗教、性生活、性取向、健康和其他生物識別信息,並補充說該應用程式本質上是一個數據經紀人,旨在將遊戲開發商與願意安裝並花錢購買手機遊戲的用戶相匹配。 Freecash 上推廣的遊戲包括《大富翁圍棋》和《迪士尼紙牌》等。 《連線》1 月的一份報告發現 Freecash 使用欺騙性行銷手段並誘使用戶在遊戲中花錢,作為回應,TikTok 撤下了 Freecash 的一些廣告,稱該公司違反了有關財務虛假陳述的規定。當時,自由現金否認參與其中,稱這些廣告是由第三方附屬公司而非其自身製作的。 週一,在 TechCrunch 聯繫蘋果公司徵求意見後,蘋果將 Freecash 從其 App Store 下架。截至週一下午,該應用程式仍在 Google Play 商店中列出。 (此後已被刪除)。 螢幕截圖圖片來源:自由現金網站截圖 當聯繫到擁有 Freecash 的德國公司 Almedia 發表評論時,該公司否認了為其平台帶來人為流量或使用欺騙性行銷技術的指控。 Techcrunch 活動 加州舊金山 | 2026年10月13-15日 Almedia 公關經理…

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *