Shanraq.org Shanraq.org
CSV: өріс ішіндегі үтір және Excel қоятын белгі
IT

Python: деректен өз есебіңізге дейін 12-сабақ (барлығы 56)

CSV: өріс ішіндегі үтір және Excel қоятын белгі

Python курсының он екінші сабағы. `2022,15.0,"шок, соғыс, логистика"` жолында `split(",")` үштің орнына бес бөлік береді — өлшенді. csv модулі үшеу береді. Қоса `DictReader`, міндетті `newline=""` және Excel белгісі: бағана орнында тұрса да, `row["year"]` `KeyError` жауабын береді.

Не үшін керек

Өткен сабақта жолды partition(";") арқылы талдадық, әрі дерек қарапайым болғанда бұл жұмыс істеді. Нағыз деректер экспорты қарапайым болмайды.

Өріске үтір түседі: «шок, соғыс, логистика». Тырнақшалар пайда болады, ал тырнақшаның ішінде — тағы үтір. Кейде ұяшыққа жол ауыстыру түседі де, бір жазба файлдың екі жолын алады.

Мұның бәрі — заңды CSV, әрі мұның бәрі split-ті сындырады. csv модулі пішімнің ережелерін түгел біледі, сондықтан деректер экспортын қолмен емес, сонымен оқиды.

Бірден тұтас

csvdemo.py файлы. Іске қосу: ортадан python csvdemo.py.

Міндеттісі — csv.writer арқылы жазу және csv.DictReader арқылы оқу. Ортаңғы блок — дәлел: онда split пен ережемен оқудың айырмасы көрінеді.

"""12-сабақ: CSV — «үтірлері бар жол» емес, өз ережелері бар пішім.

Өріс ішіндегі үтір, тырнақшалар, ұяшықтағы жол ауыстыру — бәрі заңды.
Сондықтан деректер экспортын split(",") емес, csv модулі оқиды.
"""

import csv
from pathlib import Path

HERE = Path(__file__).parent
data = HERE / "vygruzka.csv"

rows = [
    ["year", "value", "note"],
    ["2021", "8.0", "кәдімгі жыл"],
    ["2022", "15.0", "шок, соғыс, логистика"],
    ["2023", "n/a", "дерек жоқ"],
]

# newline="" міндетті: файл ішіндегі жол ауыстыруды csv модулінің өзі қояды.
with data.open("w", encoding="utf-8", newline="") as target:
    csv.writer(target).writerows(rows)

print("== файлда не жатыр")
print(data.read_text(encoding="utf-8"), end="")

print()
print("== үшінші жолда split пен csv.reader")
line = data.read_text(encoding="utf-8").splitlines()[2]
print("split(','): ", line.split(","))
with data.open(encoding="utf-8", newline="") as source:
    print("csv.reader: ", list(csv.reader(source))[2])

print()
print("== DictReader: жол — сөздік")
total = 0.0
count = 0
with data.open(encoding="utf-8", newline="") as source:
    for row in csv.DictReader(source):
        if row["value"] == "n/a":
            print(f"{row['year']}: олқылық — {row['note']}")
            continue
        total += float(row["value"])
        count += 1
        print(f"{row['year']}: {row['value']}% — {row['note']}")
print(f"алынған жол: {count}, орташа {total / count:.2f}%")

data.unlink()

Шығатыны:

== файлда не жатыр
year,value,note
2021,8.0,кәдімгі жыл
2022,15.0,"шок, соғыс, логистика"
2023,n/a,дерек жоқ

== үшінші жолда split пен csv.reader
split(','):  ['2022', '15.0', '"шок', ' соғыс', ' логистика"']
csv.reader:  ['2022', '15.0', 'шок, соғыс, логистика']

== DictReader: жол — сөздік
2021: 8.0% — кәдімгі жыл
2022: 15.0% — шок, соғыс, логистика
2023: олқылық — дерек жоқ
алынған жол: 2, орташа 11.50%

Талдау

Бір жолдағы дәлел

split(','):  ['2022', '15.0', '"шок', ' соғыс', ' логистика"']
csv.reader:  ['2022', '15.0', 'шок, соғыс, логистика']

Үштің орнына бес бөлік, шетінде тырнақшаның сынықтары, басында бос орындар. Әрі қарай бағдарламада бұл «мән» мен «ескертпе» болып кетеді, ал типтерді тексеру құтқармайды: жолдар нағыз жол ғой.

csv.reader ережені біледі: тырнақша ішіндегі үтір — бөлгіш емес, мәннің бөлігі. Тырнақшаның өзін де ол алып тастайды, өйткені олар деректікі емес, пішімдікі.

Елестетіп көріңіз. Сауалнамадағы «Алматы, Абай к-сі, 10» деген мекенжай. Адам бір мекенжайды көреді; үтір бойынша кесетін бағдарлама — үш өрісті. CSV-дегі тырнақша — сауалнамада жоқ сол жақша.

Тырнақшаны сіз емес, жазушы қояды

Файлға қараңыз: үшінші жолды csv.writer тырнақшаға өзі алды, қалғанын — жоқ. Ереже қарапайым: тырнақша файлды бір мағыналы оқу мүмкін болмайтын жерде пайда болады — мәннің ішінде бөлгіш, тырнақша немесе жол ауыстыру болса.

Осыдан тәжірибелік қорытынды: CSV-ды жолдарды жабыстырып жинамаңыз. ",".join(values) ішінде үтірі бар алғашқы мәнде-ақ сынатын файл береді — әрі сізде емес, оны ашқан адамда сынады.

newline="" — әшекей емес

with data.open("w", encoding="utf-8", newline="") as target:

csv модулі жолды немен аяқтауды өзі шешеді әрі файл оған кедергі жасамайды деп күтеді. newline="" болмаса, Python үстінен өз жол ауыстыруын қосады — Windows-та жазбалар арасында бос жолдар пайда болады, ал Excel-де ашылған файл бір-бірден оқылады.

Ережені тұтас есте сақтайды: csv үшін файлды newline=""-мен ашады — жазуға да, оқуға да. Бұл модуль құжаттамасында бірінші ескертпе болып тұр.

DictReader: бағана нөмірмен емес, атымен

for row in csv.DictReader(source):
    ... row["value"] ...

csv.reader тізім береді: row[1] — екінші өріс. Дереккөз ортасына бағана қоя салса болды — бүкіл талдау үнсіз жылжиды.

DictReader бірінші жолды баған атаулары ретінде оқып, әр жолды сөздік түрінде береді: row["value"] қажетті бағанды оның орналасқан орнына қарамастан табады. Басқа жүйелерден экспортталған деректер үшін бұл — дұрыс әдепкі таңдау, өйткені файлдағы бағандардың реті ескертусіз өзгеруі мүмкін.

Қасында csv.DictWriter тұр — ол сөздіктерді кері жазады әрі fieldnames-ті алдын ала атауды талап етеді, яғни бағаналардың реті туралы келісуді.

Бөлгіш үтір болмауы да мүмкін

Қазақстан мен Ресейде Excel-ден шыққан деректер экспорты көбіне нүктелі үтірмен келеді: ондық бөлгіші үтір болатын локальда 1,5 әйтпесе екі ұяшыққа ыдырап кетер еді. Модуль мұны біледі:

csv.reader(source, delimiter=";")
csv.DictReader(source, delimiter=";")

csv.Sniffer та бар — ол файлдың басына қарап бөлгішті болжауға тырысады. Болжағаны жаман емес, бірақ шағын не оғаш файлда қателеседі — ал талдаудағы үнсіз қате анығынан жаман. Файлға бір рет көзбен қарап, бөлгішті тікелей жазған сенімдірек.

Excel белгісі: бірінші бағана жоғалады

Excel CSV-ды файлдың басындағы көрінбейтін белгімен сақтайды:

басының байттары: b'\xef\xbb\xbfyea'

Осы үш байт — BOM, «байт реті белгісі». Мұндай файлды кәдімгі utf-8 деп оқыңыз — ол бірінші бағананың атына жабысады:

utf-8 болғандағы кілттер:      ['year', 'value']
row['year'] → KeyError 'year'

Бағана орнында, көзбен бәрі көрініп тұр, ал бағдарлама оны таппайды. Кодтау атындағы бір әріппен емделеді:

utf-8-sig болғандағы кілттер:  ['year', 'value'] | row['year'] = 2021

utf-8-sig — сол UTF-8, бірақ ол бастағы белгіні жеп қояды. Excel-ден келген файлдарға соны алады; жазғанда, керісінше, белгіні өзіміз қоспау үшін кәдімгі utf-8 қалдырады.

Сабақ картасы

Сабақ картасы: бөлгіш, тырнақша және тақырып

Өз сөзіңізбен айтыңыз

Қарамай, дауыстап немесе қағазға жауап беріңіз. Жауаптары — сабақтың соңында.

  1. split(",") жолды неге үштің орнына бес бөлікке бөлді?
  2. Файл басқа жүйеден келгенде DictReader reader-ден немен жақсы?
  3. BOM деген не, әрі оның кесірінен row["year"] неге KeyError береді?

Жаттығу

Тапсырмаға дейінгі үш қысқа қадам: болжау, толықтыру, жөндеу. Жауаптары — сабақтың соңында, бірақ алдымен өзіңіз жауап беріңіз.

1. Болжаңыз. Неше кесек шығады?

line = '2022,15.0,"шок, соғыс, логистика"'
print(len(line.split(",")))

2. Бос орынды толтырыңыз. Файлды Excel сақтаған. ... орнына бірінші баған атының белгісіз келмейтін кодтауын қойыңыз.

import csv
from pathlib import Path

HERE = Path(__file__).parent
data = HERE / "excel.csv"
data.write_bytes("year,value\n2021,8.0\n".encode("utf-8-sig"))
with data.open(encoding=..., newline="") as source:
    for row in csv.DictReader(source):
        print(row["year"])
data.unlink()

3. Жөндеңіз. Жол жабыстырып жазылған, әрі оқығанда өріс жазғаннан көп болып шықты. Оны тиісінше құрыңыз.

import csv
from pathlib import Path

HERE = Path(__file__).parent
data = HERE / "vygruzka.csv"
row = ["2022", "15.0", "шок, соғыс, логистика"]

data.write_text(",".join(row) + "\n", encoding="utf-8")
with data.open(encoding="utf-8", newline="") as source:
    for back in csv.reader(source):
        print("өріс:", len(back))
data.unlink()

Тапсырма

Міндетті. Берілгені:

rows = [
    ["month", "price", "note"],
    ["қаңтар", "520", "әдеттегі ай"],
    ["ақпан", "546,5", "шок, сұраныс, логистика"],
    ["наурыз", "n/a", "бағасы жоқ"],
]

Мұны newline=""-пен csv.writer арқылы vygruzka.csv-ке жазыңыз. Файлдың үшінші жолында split(",") неше кесек беретінін және csv.reader неше өріс көретінін басып шығарыңыз. Содан кейін файлды DictReader-мен аралаңыз: n/a тұрған айды ескертпесімен бірге жетіспейтін мән деп, қалғанын бағасы мен ескертпесімен басып шығарыңыз, ал соңында неше ай алынғанын және екі таңбалы орташаны басыңыз (сандағы үтір нүкте деп саналады). Файлды өзіңізден кейін жинаңыз.

Күтілетін шығу:

split(','): 6 кесек
csv.reader: 3 өріс
қаңтар: 520 — әдеттегі ай
ақпан: 546,5 — шок, сұраныс, логистика
наурыз: олқылық — бағасы жоқ
алынған ай: 2, орташа 533.25

Дайын болғаны: шығу жол-жолмен сәйкес келеді; файл жабыстырып емес, csv.writer-мен құралған; бағандар нөмірімен емес, атымен алынған; newline="" жазуда да, оқуда да тұр.

Өз деректеріңізде. csv.writer арқылы өз деректеріңізден файл жинаңыз, бір өрісінде міндетті түрде үтір болсын. Оны екі тәсілмен оқыңыз: split(",") және csv.reader — әрі екі нәтижені қатар басып шығарыңыз. Содан кейін файлды DictReader-мен аралап, n/a-ны аттап өтіп, сандық бағана бойынша орташаны санаңыз.

Мұның бәрі step-5 ішінде жиналған — өзіңіз жазып болғаннан кейін салыстырыңыз.

Қалауыңызша.

  • Файлды delimiter=";"-мен жазып, оны Excel-де немесе LibreOffice-те ашыңыз.
  • Excel-ден кириллицасы бар файл сақтап, оны алдымен utf-8, содан кейін utf-8-sig деп оқыңыз.
  • Жазу кезінде newline=""-ды алып тастап, файлға көрінбейтін таңбаларды көрсететін редактордан қараңыз.

Жобада бұл қайда тұрады

Шолу деректі кім, қалай деректер экспорты жасағанына тәуелді болуын қояды. Файлды DictReader бағана аттары бойынша оқиды, бөлгіш анық аталған, Excel белгісі бірінші бағананы сындырмайды, ал n/a жазылған жол орташаға емес, жетіспейтін мәндерге кетеді.

Қарыздар. Біз файлды циклмен оқысақ та, талданғанын жадта ұстаймыз. Жүз мыңдаған жолдық деректер экспорты үшін бұл көп — ондайды қалай оқу керегіне генераторлар сабағында ораламыз.

Жауаптар

Жауаптарды көрсету

Сұрақтарға

  1. Өйткені өрісте үтір болды, ал split тырнақша туралы білмейді: ол әр бөлгіш таңба бойынша кеседі. csv.reader пішімнің ережесін біледі — тырнақша ішіндегі үтір мәнге тиесілі.
  2. Ол алғашқы жолды тақырып ретінде алып, сөздік береді, сондықтан бағана атымен табылады. Дереккөз ортасына бағана қойса, нөмір бойынша талдау үнсіз сынады, ал ат бойынша жұмысын жалғастырады.
  3. Бұл — Excel кодтау белгісі ретінде жазатын, файл басындағы үш көрінбейтін байт. Кәдімгі utf-8 деп оқығанда олар бірінші бағананың атына жабысады да, ол өзінің нағыз аты бойынша табылмай қалады. Мұндай файлдарды utf-8-sig деп оқиды.

Жаттығуға

  1. Бесеу. Бөлетін үтір екеу, ал ескертпенің ішінде тағы екеу бар, әрі split төртеуін де кеседі: ол тырнақшаны білмейді. Сол жолда csv.reader үш өріс береді.
5
  1. "utf-8-sig". Кәдімгі utf-8 Excel белгісін бірінші баған атына жабыстырып қалдырады да, баған тұрса да row["year"] KeyError береді.
import csv
from pathlib import Path

HERE = Path(__file__).parent
data = HERE / "excel.csv"
data.write_bytes("year,value\n2021,8.0\n".encode("utf-8-sig"))
with data.open(encoding="utf-8-sig", newline="") as source:
    for row in csv.DictReader(source):
        print(row["year"])
data.unlink()
2021
  1. ",".join(row) ескертпенің үтірлерін бөлгіш қылып жазды, әрі оқығанда өріс үшеудің орнына бесеу болды. Тырнақшаны жазушы қояды — csv.writer:
import csv
from pathlib import Path

HERE = Path(__file__).parent
data = HERE / "vygruzka.csv"
row = ["2022", "15.0", "шок, соғыс, логистика"]

with data.open("w", encoding="utf-8", newline="") as target:
    csv.writer(target).writerow(row)
with data.open(encoding="utf-8", newline="") as source:
    for back in csv.reader(source):
        print("өріс:", len(back))
data.unlink()
өріс: 3

Дереккөздер

Мәтінде қате не теру қатесі кездессе, бізге айтыңыз

Тапсырманы тексеру

Алдымен VS Code-та шешіп, іске қосыңыз — редактор қатені сол жерде көрсетеді. Дайын шешімді осында қойыңыз. Тексеретін — модель: ол қатені атап көрсетеді, бірақ дайын жауапты бермейді.

Тексеру үшін кіру керек. Кіру

Пікірлер (0)

Әзірге пікір жоқ. Бірінші болыңыз.