[{"data":1,"prerenderedAt":2002},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-csv-exports-with-semicolons-and-decimal-commas":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-csv-exports-with-semicolons-and-decimal-commas":1967,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-csv-exports-with-semicolons-and-decimal-commas":1976,"children:\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-csv-exports-with-semicolons-and-decimal-commas":1981,"related:\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-csv-exports-with-semicolons-and-decimal-commas":1982},{"id":4,"title":5,"body":6,"dateModified":1932,"datePublished":1932,"description":1933,"extension":1934,"faq":1935,"keyword":1944,"kind":1945,"meta":1946,"navigation":182,"path":1955,"seo":1956,"slug":1959,"stem":1960,"topics":1961,"type":1965,"__hash__":1966},"docs\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-csv-exports-with-semicolons-and-decimal-commas\u002Findex.md","Read European CSV Exports with Semicolons and Decimal Commas",{"type":7,"value":8,"toc":1917},"minimark",[9,36,104,109,137,147,151,154,296,318,322,339,423,449,678,682,694,824,854,858,864,1074,1085,1088,1228,1232,1239,1382,1392,1396,1399,1614,1627,1631,1634,1724,1735,1739,1789,1793,1814,1818,1852,1856,1862,1868,1874,1880,1884,1913],[10,11,12,13,17,18,21,22,25,26,29,30,35],"p",{},"A CSV export from a German, French, Dutch or Scandinavian system arrives, and ",[14,15,16],"code",{},"pd.read_csv"," either fails outright or produces something odd: a ",[14,19,20],{},"UnicodeDecodeError"," on the first ",[14,23,24],{},"ü",", a single column holding entire lines, amounts like ",[14,27,28],{},"1.234,56"," stuck as text. The file is fine. It follows the conventions of a locale where the comma is the decimal separator — so fields are separated by semicolons, numbers use a decimal comma and a dot for thousands, and the text is often in a Windows code page rather than UTF-8. pandas can read all of it, with the right options, and one of those options has a side effect that silently corrupts other columns. This page, part of ",[31,32,34],"a",{"href":33},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002F","Handling Excel File Formats and Conversions",", reads such a file correctly and writes it to Excel.",[37,38,46,47,46,51,46,55,46,63,46,70,46,78,46,83,46,86,46,90,46,93,46,97,46,100],"svg",{"viewBox":39,"role":40,"ariaLabelledBy":41,"xmlns":44,"style":45},"175 0 410 176","img",[42,43],"eu-raw-t","eu-raw-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:410px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[48,49,50],"title",{"id":42},"What a European CSV export looks like",[52,53,54],"desc",{"id":43},"The raw lines of export.csv. Fields are separated by semicolons, a customer name containing a semicolon is quoted, the date is written 31.01.2026, the amount 1.234,56 uses a dot for thousands and a comma for decimals, and the postcode 01067 has a leading zero.",[56,57],"rect",{"x":58,"y":59,"width":60,"height":61,"fill":62},"175","0","410","176","#ffffff",[64,65,69],"text",{"x":66,"y":67,"style":68},"380.0","31.0","font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","export.csv — cp1252, semicolon-separated",[56,71],{"x":72,"y":73,"width":74,"height":75,"rx":59,"fill":62,"stroke":76,"style":77},"191.3","48.0","377.5","28.0","var(--line,#cdd5e6)","stroke-width:1px",[64,79,82],{"x":66,"y":80,"style":81},"67.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","Kunde;Datum;Betrag;Menge;PLZ;Version",[56,84],{"x":72,"y":85,"width":74,"height":75,"rx":59,"fill":62,"stroke":76,"style":77},"76.0",[64,87,89],{"x":66,"y":88,"style":81},"95.0","\"Müller; Sohn GmbH\";31.01.2026;1.234,56;3;01067;1.5.2",[56,91],{"x":72,"y":92,"width":74,"height":75,"rx":59,"fill":62,"stroke":76,"style":77},"104.0",[64,94,96],{"x":66,"y":95,"style":81},"123.0","Schmidt AG;01.02.2026;-12,5;10;80331;2.0",[56,98],{"x":72,"y":99,"width":74,"height":75,"rx":59,"fill":62,"stroke":76,"style":77},"132.0",[64,101,103],{"x":66,"y":102,"style":81},"151.0","Weber KG;15.02.2026;980,00;1;10115;2.1",[105,106,108],"h2",{"id":107},"prerequisites","Prerequisites",[110,111,116],"pre",{"className":112,"code":113,"language":114,"meta":115,"style":115},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl\n","bash","",[14,117,118],{"__ignoreMap":115},[119,120,123,127,131,134],"span",{"class":121,"line":122},"line",1,[119,124,126],{"class":125},"sMTad","pip",[119,128,130],{"class":129},"srMev"," install",[119,132,133],{"class":129}," pandas",[119,135,136],{"class":129}," openpyxl\n",[10,138,139,142,143,146],{},[14,140,141],{},"export.csv"," is encoded in Windows-1252, separates fields with semicolons, quotes one customer name that contains a semicolon, and writes dates as ",[14,144,145],{},"DD.MM.YYYY",", amounts with a decimal comma and thousands dots, postcodes with leading zeros, and a version string.",[105,148,150],{"id":149},"step-1-fix-the-encoding-and-the-separator","Step 1 — Fix the encoding and the separator",[10,152,153],{},"The default read fails before it gets to the data:",[110,155,159],{"className":156,"code":157,"language":158,"meta":115,"style":115},"language-python shiki shiki-themes github-light github-dark-high-contrast","import pandas as pd\n\ntry:\n    pd.read_csv(\"export.csv\")\nexcept UnicodeDecodeError as exc:\n    print(\"UnicodeDecodeError:\", exc.reason)\n\nraw = pd.read_csv(\"export.csv\", sep=\";\", encoding=\"cp1252\")\nprint(raw.dtypes.to_dict())\nprint(raw)\n","python",[14,160,161,177,184,193,205,221,236,241,279,288],{"__ignoreMap":115},[119,162,163,167,171,174],{"class":121,"line":122},[119,164,166],{"class":165},"s-kum","import",[119,168,170],{"class":169},"skGVy"," pandas ",[119,172,173],{"class":165},"as",[119,175,176],{"class":169}," pd\n",[119,178,180],{"class":121,"line":179},2,[119,181,183],{"emptyLinePlaceholder":182},true,"\n",[119,185,187,190],{"class":121,"line":186},3,[119,188,189],{"class":165},"try",[119,191,192],{"class":169},":\n",[119,194,196,199,202],{"class":121,"line":195},4,[119,197,198],{"class":169},"    pd.read_csv(",[119,200,201],{"class":129},"\"export.csv\"",[119,203,204],{"class":169},")\n",[119,206,208,211,215,218],{"class":121,"line":207},5,[119,209,210],{"class":165},"except",[119,212,214],{"class":213},"sP0c6"," UnicodeDecodeError",[119,216,217],{"class":165}," as",[119,219,220],{"class":169}," exc:\n",[119,222,224,227,230,233],{"class":121,"line":223},6,[119,225,226],{"class":213},"    print",[119,228,229],{"class":169},"(",[119,231,232],{"class":129},"\"UnicodeDecodeError:\"",[119,234,235],{"class":169},", exc.reason)\n",[119,237,239],{"class":121,"line":238},7,[119,240,183],{"emptyLinePlaceholder":182},[119,242,244,247,250,253,255,258,262,264,267,269,272,274,277],{"class":121,"line":243},8,[119,245,246],{"class":169},"raw ",[119,248,249],{"class":165},"=",[119,251,252],{"class":169}," pd.read_csv(",[119,254,201],{"class":129},[119,256,257],{"class":169},", ",[119,259,261],{"class":260},"sa561","sep",[119,263,249],{"class":165},[119,265,266],{"class":129},"\";\"",[119,268,257],{"class":169},[119,270,271],{"class":260},"encoding",[119,273,249],{"class":165},[119,275,276],{"class":129},"\"cp1252\"",[119,278,204],{"class":169},[119,280,282,285],{"class":121,"line":281},9,[119,283,284],{"class":213},"print",[119,286,287],{"class":169},"(raw.dtypes.to_dict())\n",[119,289,291,293],{"class":121,"line":290},10,[119,292,284],{"class":213},[119,294,295],{"class":169},"(raw)\n",[10,297,298,301,302,305,306,309,310,313,314,317],{},[14,299,300],{},"encoding=\"cp1252\""," reads the umlauts; ",[14,303,304],{},"sep=\";\""," splits the columns, and the quoted ",[14,307,308],{},"\"Müller; Sohn GmbH\""," stays in one cell because the CSV parser respects quotes. Files saved as \"CSV UTF-8\" by newer Excel versions need ",[14,311,312],{},"encoding=\"utf-8-sig\""," instead, which also strips the byte-order mark that would otherwise end up glued to the first column name. The amounts, dates and version are still text, and the postcode ",[14,315,316],{},"01067"," has become the integer 1067.",[105,319,321],{"id":320},"step-2-parse-the-numbers-and-see-what-thousands-does-to-everything-else","Step 2 — Parse the numbers, and see what thousands does to everything else",[10,323,324,327,328,331,332,334,335,338],{},[14,325,326],{},"decimal=\",\""," tells pandas the comma is the decimal separator, and ",[14,329,330],{},"thousands=\".\""," that dots group thousands. Together they turn ",[14,333,28],{}," into ",[14,336,337],{},"1234.56",". But the thousands option is not limited to the amount column — pandas applies it to every column it tries to read as a number:",[110,340,342],{"className":156,"code":341,"language":158,"meta":115,"style":115},"naive = pd.read_csv(\"export.csv\", sep=\";\", encoding=\"cp1252\", decimal=\",\", thousands=\".\")\nprint(naive.dtypes.to_dict())\nprint(naive[[\"Datum\", \"Betrag\", \"Version\"]])\n",[14,343,344,393,400],{"__ignoreMap":115},[119,345,346,349,351,353,355,357,359,361,363,365,367,369,371,373,376,378,381,383,386,388,391],{"class":121,"line":122},[119,347,348],{"class":169},"naive ",[119,350,249],{"class":165},[119,352,252],{"class":169},[119,354,201],{"class":129},[119,356,257],{"class":169},[119,358,261],{"class":260},[119,360,249],{"class":165},[119,362,266],{"class":129},[119,364,257],{"class":169},[119,366,271],{"class":260},[119,368,249],{"class":165},[119,370,276],{"class":129},[119,372,257],{"class":169},[119,374,375],{"class":260},"decimal",[119,377,249],{"class":165},[119,379,380],{"class":129},"\",\"",[119,382,257],{"class":169},[119,384,385],{"class":260},"thousands",[119,387,249],{"class":165},[119,389,390],{"class":129},"\".\"",[119,392,204],{"class":169},[119,394,395,397],{"class":121,"line":179},[119,396,284],{"class":213},[119,398,399],{"class":169},"(naive.dtypes.to_dict())\n",[119,401,402,404,407,410,412,415,417,420],{"class":121,"line":186},[119,403,284],{"class":213},[119,405,406],{"class":169},"(naive[[",[119,408,409],{"class":129},"\"Datum\"",[119,411,257],{"class":169},[119,413,414],{"class":129},"\"Betrag\"",[119,416,257],{"class":169},[119,418,419],{"class":129},"\"Version\"",[119,421,422],{"class":169},"]])\n",[10,424,425,428,429,432,433,436,437,440,441,444,445,448],{},[14,426,427],{},"Betrag"," is right. ",[14,430,431],{},"Datum"," is now the integer ",[14,434,435],{},"31012026",", and ",[14,438,439],{},"Version"," has become ",[14,442,443],{},"152"," and ",[14,446,447],{},"20"," — the dots were removed as thousands separators and the remaining digits parsed as numbers. No warning is printed. A report built on this file would show nonsense dates, or sort versions wrongly, and nothing in the job's log would say why.",[37,450,46,456,46,459,46,462,46,466,46,473,46,477,46,483,46,487,46,491,46,495,46,499,46,502,46,507,46,509,46,512,46,514,46,516,46,518,46,520,46,523,46,527,46,529,46,532,46,534,46,536,46,538,46,541,46,544,46,548,46,550,46,553,46,555,46,558,46,560,46,563,46,566,46,570,46,573,46,577,46,580,46,583,46,586,46,588,46,591,46,593,46,595,46,597,46,599,46,601,46,603,46,605,46,607,46,612,46,615,46,619,46,622,46,624,46,626,46,628,46,630,46,632,46,635,46,637,46,640,46,642,46,644,46,650,46,654,46,661,46,666,46,674],{"viewBox":451,"role":40,"ariaLabelledBy":452,"xmlns":44,"style":455},"20 0 720 164",[453,454],"eu-trap-t","eu-trap-d","width:100%;max-width:720px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[48,457,458],{"id":453},"What thousands=\".\" does to non-number columns",[52,460,461],{"id":454},"Left: the raw text values 31.01.2026, 1.234,56 and 2.0 in the Datum, Betrag and Version columns. Right: after read_csv with decimal and thousands options, Betrag is correctly 1234.56, but Datum has become the integer 31012026 and Version the integer 20.",[56,463],{"x":447,"y":59,"width":464,"height":465,"fill":62},"720","164",[56,467],{"x":468,"y":469,"width":470,"height":471,"rx":59,"fill":472,"stroke":76,"style":77},"36.5","40.0","32.0","24.0","#e7ebef",[56,474],{"x":475,"y":469,"width":476,"height":471,"rx":59,"fill":472,"stroke":76,"style":77},"68.5","95.3",[64,478,482],{"x":479,"y":480,"style":481},"116.1","57.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[56,484],{"x":485,"y":469,"width":486,"height":471,"rx":59,"fill":472,"stroke":76,"style":77},"163.8","78.5",[64,488,490],{"x":489,"y":480,"style":481},"203.0","B",[56,492],{"x":493,"y":469,"width":494,"height":471,"rx":59,"fill":472,"stroke":76,"style":77},"242.3","72.1",[64,496,498],{"x":497,"y":480,"style":481},"278.3","C",[56,500],{"x":468,"y":501,"width":470,"height":75,"rx":59,"fill":472,"stroke":76,"style":77},"64.0",[64,503,506],{"x":504,"y":505,"style":481},"52.5","83.0","1",[56,508],{"x":475,"y":501,"width":476,"height":75,"rx":59,"fill":62,"stroke":76,"style":77},[64,510,431],{"x":479,"y":505,"style":511},"font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:middle",[56,513],{"x":485,"y":501,"width":486,"height":75,"rx":59,"fill":62,"stroke":76,"style":77},[64,515,427],{"x":489,"y":505,"style":511},[56,517],{"x":493,"y":501,"width":494,"height":75,"rx":59,"fill":62,"stroke":76,"style":77},[64,519,439],{"x":497,"y":505,"style":511},[56,521],{"x":468,"y":522,"width":470,"height":75,"rx":59,"fill":472,"stroke":76,"style":77},"92.0",[64,524,526],{"x":504,"y":525,"style":481},"111.0","2",[56,528],{"x":475,"y":522,"width":476,"height":75,"rx":59,"fill":62,"stroke":76,"style":77},[64,530,531],{"x":479,"y":525,"style":81},"31.01.2026",[56,533],{"x":485,"y":522,"width":486,"height":75,"rx":59,"fill":62,"stroke":76,"style":77},[64,535,28],{"x":489,"y":525,"style":81},[56,537],{"x":493,"y":522,"width":494,"height":75,"rx":59,"fill":62,"stroke":76,"style":77},[64,539,540],{"x":497,"y":525,"style":81},"1.5.2",[56,542],{"x":468,"y":543,"width":470,"height":75,"rx":59,"fill":472,"stroke":76,"style":77},"120.0",[64,545,547],{"x":504,"y":546,"style":481},"139.0","3",[56,549],{"x":475,"y":543,"width":476,"height":75,"rx":59,"fill":62,"stroke":76,"style":77},[64,551,552],{"x":479,"y":546,"style":81},"01.02.2026",[56,554],{"x":485,"y":543,"width":486,"height":75,"rx":59,"fill":62,"stroke":76,"style":77},[64,556,557],{"x":489,"y":546,"style":81},"-12,5",[56,559],{"x":493,"y":543,"width":494,"height":75,"rx":59,"fill":62,"stroke":76,"style":77},[64,561,562],{"x":497,"y":546,"style":81},"2.0",[56,564],{"x":565,"y":469,"width":470,"height":471,"rx":59,"fill":472,"stroke":76,"style":77},"461.1",[56,567],{"x":568,"y":469,"width":569,"height":471,"rx":59,"fill":472,"stroke":76,"style":77},"493.1","84.0",[64,571,482],{"x":572,"y":480,"style":481},"535.2",[56,574],{"x":575,"y":469,"width":576,"height":471,"rx":59,"fill":472,"stroke":76,"style":77},"577.2","74.2",[64,578,490],{"x":579,"y":480,"style":481},"614.3",[56,581],{"x":582,"y":469,"width":494,"height":471,"rx":59,"fill":472,"stroke":76,"style":77},"651.4",[64,584,498],{"x":585,"y":480,"style":481},"687.5",[56,587],{"x":565,"y":501,"width":470,"height":75,"rx":59,"fill":472,"stroke":76,"style":77},[64,589,506],{"x":590,"y":505,"style":481},"477.1",[56,592],{"x":568,"y":501,"width":569,"height":75,"rx":59,"fill":62,"stroke":76,"style":77},[64,594,431],{"x":572,"y":505,"style":511},[56,596],{"x":575,"y":501,"width":576,"height":75,"rx":59,"fill":62,"stroke":76,"style":77},[64,598,427],{"x":579,"y":505,"style":511},[56,600],{"x":582,"y":501,"width":494,"height":75,"rx":59,"fill":62,"stroke":76,"style":77},[64,602,439],{"x":585,"y":505,"style":511},[56,604],{"x":565,"y":522,"width":470,"height":75,"rx":59,"fill":472,"stroke":76,"style":77},[64,606,526],{"x":590,"y":525,"style":481},[56,608],{"x":568,"y":522,"width":569,"height":75,"rx":59,"fill":609,"stroke":610,"style":611},"#fee8f2","var(--accent,#f43f8f)","stroke-width:1.5px",[64,613,435],{"x":572,"y":525,"style":614},"font-size:12px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle",[56,616],{"x":575,"y":522,"width":576,"height":75,"rx":59,"fill":617,"stroke":618,"style":611},"#d9f4f1","var(--teal,#0f9488)",[64,620,337],{"x":579,"y":525,"style":621},"font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle",[56,623],{"x":582,"y":522,"width":494,"height":75,"rx":59,"fill":609,"stroke":610,"style":611},[64,625,443],{"x":585,"y":525,"style":614},[56,627],{"x":565,"y":543,"width":470,"height":75,"rx":59,"fill":472,"stroke":76,"style":77},[64,629,547],{"x":590,"y":546,"style":481},[56,631],{"x":568,"y":543,"width":569,"height":75,"rx":59,"fill":609,"stroke":610,"style":611},[64,633,634],{"x":572,"y":546,"style":614},"1022026",[56,636],{"x":575,"y":543,"width":576,"height":75,"rx":59,"fill":617,"stroke":618,"style":611},[64,638,639],{"x":579,"y":546,"style":621},"-12.5",[56,641],{"x":582,"y":543,"width":494,"height":75,"rx":59,"fill":609,"stroke":610,"style":611},[64,643,447],{"x":585,"y":546,"style":614},[64,645,649],{"x":646,"y":647,"style":648},"175.4","29.0","font-size:12.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","text in the file",[64,651,653],{"x":652,"y":647,"style":648},"592.3","what pandas returns",[121,655],{"x1":656,"y1":657,"x2":658,"y2":657,"stroke":659,"style":660},"324.4","94.0","447.1","var(--brand,#5b5cf0)","stroke-width:2px",[662,663],"polygon",{"points":664,"fill":665},"451.1,94.0 442.1,99.0 442.1,89.0","#5b5cf0",[56,667],{"x":668,"y":669,"width":670,"height":671,"rx":672,"fill":62,"stroke":76,"style":673},"345.0","61.0","85.4","22.0","11","stroke-width:1.2px",[64,675,385],{"x":676,"y":85,"style":677},"387.7","font-size:11.5px;font-weight:400;fill:var(--brand-strong,#4338ca);text-anchor:middle;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace",[105,679,681],{"id":680},"step-3-protect-text-like-columns-with-dtype-and-parse-dates-explicitly","Step 3 — Protect text-like columns with dtype and parse dates explicitly",[10,683,684,685,689,690,693],{},"The fix is to tell pandas which columns are ",[686,687,688],"em",{},"not"," numbers. Give text-like columns — postcodes, versions, IDs — a ",[14,691,692],{},"str"," dtype, and parse the date column as a date in the same call so it is never treated as a number:",[110,695,697],{"className":156,"code":696,"language":158,"meta":115,"style":115},"df = pd.read_csv(\n    \"export.csv\", sep=\";\", encoding=\"cp1252\",\n    decimal=\",\", thousands=\".\",\n    dtype={\"PLZ\": str, \"Version\": str},\n    parse_dates=[\"Datum\"], dayfirst=True,\n)\nprint(df.dtypes.to_dict())\nprint(df)\n",[14,698,699,709,733,752,781,806,810,817],{"__ignoreMap":115},[119,700,701,704,706],{"class":121,"line":122},[119,702,703],{"class":169},"df ",[119,705,249],{"class":165},[119,707,708],{"class":169}," pd.read_csv(\n",[119,710,711,714,716,718,720,722,724,726,728,730],{"class":121,"line":179},[119,712,713],{"class":129},"    \"export.csv\"",[119,715,257],{"class":169},[119,717,261],{"class":260},[119,719,249],{"class":165},[119,721,266],{"class":129},[119,723,257],{"class":169},[119,725,271],{"class":260},[119,727,249],{"class":165},[119,729,276],{"class":129},[119,731,732],{"class":169},",\n",[119,734,735,738,740,742,744,746,748,750],{"class":121,"line":186},[119,736,737],{"class":260},"    decimal",[119,739,249],{"class":165},[119,741,380],{"class":129},[119,743,257],{"class":169},[119,745,385],{"class":260},[119,747,249],{"class":165},[119,749,390],{"class":129},[119,751,732],{"class":169},[119,753,754,757,759,762,765,768,770,772,774,776,778],{"class":121,"line":195},[119,755,756],{"class":260},"    dtype",[119,758,249],{"class":165},[119,760,761],{"class":169},"{",[119,763,764],{"class":129},"\"PLZ\"",[119,766,767],{"class":169},": ",[119,769,692],{"class":213},[119,771,257],{"class":169},[119,773,419],{"class":129},[119,775,767],{"class":169},[119,777,692],{"class":213},[119,779,780],{"class":169},"},\n",[119,782,783,786,788,791,793,796,799,801,804],{"class":121,"line":207},[119,784,785],{"class":260},"    parse_dates",[119,787,249],{"class":165},[119,789,790],{"class":169},"[",[119,792,409],{"class":129},[119,794,795],{"class":169},"], ",[119,797,798],{"class":260},"dayfirst",[119,800,249],{"class":165},[119,802,803],{"class":213},"True",[119,805,732],{"class":169},[119,807,808],{"class":121,"line":223},[119,809,204],{"class":169},[119,811,812,814],{"class":121,"line":238},[119,813,284],{"class":213},[119,815,816],{"class":169},"(df.dtypes.to_dict())\n",[119,818,819,821],{"class":121,"line":243},[119,820,284],{"class":213},[119,822,823],{"class":169},"(df)\n",[10,825,826,827,829,830,833,834,836,837,444,840,842,843,846,847,849,850,853],{},"Every column is now what it should be: ",[14,828,427],{}," a float, ",[14,831,832],{},"Menge"," an integer, ",[14,835,431],{}," real dates, ",[14,838,839],{},"PLZ",[14,841,439],{}," text with their leading zeros and dots intact. ",[14,844,845],{},"dayfirst=True"," makes ",[14,848,552],{}," the first of February rather than the second of January. For stricter parsing, read the date column as text and convert it with an explicit format — ",[14,851,852],{},"pd.to_datetime(df[\"Datum\"], format=\"%d.%m.%Y\")"," — which raises on any value that does not match instead of guessing.",[105,855,857],{"id":856},"step-4-or-convert-the-amount-columns-yourself","Step 4 — Or convert the amount columns yourself",[10,859,860,861,863],{},"When a file has many text columns and few numeric ones, it is simpler to leave ",[14,862,385],{}," out entirely and convert just the amount columns. A small function makes the rule explicit and fails loudly on values that are not amounts:",[110,865,867],{"className":156,"code":866,"language":158,"meta":115,"style":115},"def de_number(text):\n    if pd.isna(text) or str(text).strip() == \"\":\n        return None\n    return float(str(text).strip().replace(\".\", \"\").replace(\",\", \".\"))\n\nsafe = pd.read_csv(\"export.csv\", sep=\";\", encoding=\"cp1252\", dtype=str)\nsafe[\"Betrag\"] = safe[\"Betrag\"].map(de_number)\nsafe[\"Menge\"] = safe[\"Menge\"].astype(int)\nsafe[\"Datum\"] = pd.to_datetime(safe[\"Datum\"], format=\"%d.%m.%Y\")\nprint(safe.dtypes.to_dict())\n",[14,868,869,881,906,914,948,952,990,1010,1033,1067],{"__ignoreMap":115},[119,870,871,874,878],{"class":121,"line":122},[119,872,873],{"class":165},"def",[119,875,877],{"class":876},"s_Opv"," de_number",[119,879,880],{"class":169},"(text):\n",[119,882,883,886,889,892,895,898,901,904],{"class":121,"line":179},[119,884,885],{"class":165},"    if",[119,887,888],{"class":169}," pd.isna(text) ",[119,890,891],{"class":165},"or",[119,893,894],{"class":213}," str",[119,896,897],{"class":169},"(text).strip() ",[119,899,900],{"class":165},"==",[119,902,903],{"class":129}," \"\"",[119,905,192],{"class":169},[119,907,908,911],{"class":121,"line":186},[119,909,910],{"class":165},"        return",[119,912,913],{"class":213}," None\n",[119,915,916,919,922,924,926,929,931,933,936,939,941,943,945],{"class":121,"line":195},[119,917,918],{"class":165},"    return",[119,920,921],{"class":213}," float",[119,923,229],{"class":169},[119,925,692],{"class":213},[119,927,928],{"class":169},"(text).strip().replace(",[119,930,390],{"class":129},[119,932,257],{"class":169},[119,934,935],{"class":129},"\"\"",[119,937,938],{"class":169},").replace(",[119,940,380],{"class":129},[119,942,257],{"class":169},[119,944,390],{"class":129},[119,946,947],{"class":169},"))\n",[119,949,950],{"class":121,"line":207},[119,951,183],{"emptyLinePlaceholder":182},[119,953,954,957,959,961,963,965,967,969,971,973,975,977,979,981,984,986,988],{"class":121,"line":223},[119,955,956],{"class":169},"safe ",[119,958,249],{"class":165},[119,960,252],{"class":169},[119,962,201],{"class":129},[119,964,257],{"class":169},[119,966,261],{"class":260},[119,968,249],{"class":165},[119,970,266],{"class":129},[119,972,257],{"class":169},[119,974,271],{"class":260},[119,976,249],{"class":165},[119,978,276],{"class":129},[119,980,257],{"class":169},[119,982,983],{"class":260},"dtype",[119,985,249],{"class":165},[119,987,692],{"class":213},[119,989,204],{"class":169},[119,991,992,995,997,1000,1002,1005,1007],{"class":121,"line":238},[119,993,994],{"class":169},"safe[",[119,996,414],{"class":129},[119,998,999],{"class":169},"] ",[119,1001,249],{"class":165},[119,1003,1004],{"class":169}," safe[",[119,1006,414],{"class":129},[119,1008,1009],{"class":169},"].map(de_number)\n",[119,1011,1012,1014,1017,1019,1021,1023,1025,1028,1031],{"class":121,"line":243},[119,1013,994],{"class":169},[119,1015,1016],{"class":129},"\"Menge\"",[119,1018,999],{"class":169},[119,1020,249],{"class":165},[119,1022,1004],{"class":169},[119,1024,1016],{"class":129},[119,1026,1027],{"class":169},"].astype(",[119,1029,1030],{"class":213},"int",[119,1032,204],{"class":169},[119,1034,1035,1037,1039,1041,1043,1046,1048,1050,1053,1055,1058,1062,1065],{"class":121,"line":281},[119,1036,994],{"class":169},[119,1038,409],{"class":129},[119,1040,999],{"class":169},[119,1042,249],{"class":165},[119,1044,1045],{"class":169}," pd.to_datetime(safe[",[119,1047,409],{"class":129},[119,1049,795],{"class":169},[119,1051,1052],{"class":260},"format",[119,1054,249],{"class":165},[119,1056,1057],{"class":129},"\"",[119,1059,1061],{"class":1060},"sSjpA","%d",[119,1063,1064],{"class":129},".%m.%Y\"",[119,1066,204],{"class":169},[119,1068,1069,1071],{"class":121,"line":290},[119,1070,284],{"class":213},[119,1072,1073],{"class":169},"(safe.dtypes.to_dict())\n",[10,1075,1076,1077,1079,1080,1084],{},"Reading everything as ",[14,1078,692],{}," first means nothing is inferred behind your back; each conversion is a line you can see, test and change. This is the approach to prefer for scheduled jobs, where a column added to the export next month should not quietly change type. ",[31,1081,1083],{"href":1082},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fconvert-currency-strings-to-numbers-with-pandas\u002F","Convert Currency Strings to Numbers with pandas"," covers amounts with currency symbols and brackets as well.",[10,1086,1087],{},"Which of the two approaches to use depends on the shape of the file more than on taste. The built-in options are shorter and faster; the read-as-text approach is more predictable. For a one-off conversion of a file you can inspect, Step 3 is fine. For a job that will read a new export every night, choose Step 4:",[37,1089,46,1095,46,1098,46,1101,46,1106,46,1114,46,1125,46,1130,46,1135,46,1140,46,1147,46,1151,46,1156,46,1160,46,1166,46,1170,46,1175,46,1178,46,1184,46,1189,46,1193,46,1197,46,1202,46,1205,46,1210,46,1213,46,1218,46,1221],{"viewBox":1090,"role":40,"ariaLabelledBy":1091,"xmlns":44,"style":1094},"4 0 752 233",[1092,1093],"eu-choose-t","eu-choose-d","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[48,1096,1097],{"id":1092},"Built-in number options versus reading as text",[52,1099,1100],{"id":1093},"Two panels. Using decimal and thousands with dtype protection is short and fast but requires listing every text-like column, and a new column added to the export may be misparsed. Reading every column as text and converting amounts explicitly needs a few more lines, but nothing is inferred and a new column stays text until you decide otherwise.",[56,1102],{"x":1103,"y":59,"width":1104,"height":1105,"fill":62},"4","752","233",[56,1107],{"x":1108,"y":1109,"width":1110,"height":1111,"rx":1112,"fill":1113,"stroke":659,"style":660},"20.0","16.0","351.0","201.0","14","#f0f4ff",[1115,1116,1118],"g",{"transform":1117},"translate(182.5,29.0) scale(1.083)",[1119,1120],"path",{"d":1121,"fill":1122,"stroke":1123,"style":1124},"M13 2L4 14h7l-1 8 9-12h-7z","none","var(--brand-strong,#4338ca)","stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[64,1126,1129],{"x":1127,"y":1128,"style":68},"195.5","78.0","decimal=\",\" thousands=\".\" + dtype",[121,1131],{"x1":1132,"y1":1133,"x2":1134,"y2":1133,"stroke":659,"style":77},"36.0","91.0","355.0",[64,1136,1139],{"x":1137,"y":525,"style":1138},"60.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:start","one read_csv call",[1115,1141,1143],{"transform":1142},"translate(38.5,99.5) scale(0.625)",[1119,1144],{"d":1145,"fill":1122,"stroke":618,"style":1146},"M5 12.5l4.5 4.5L19 7","stroke-width:2.88px;stroke-linecap:round;stroke-linejoin:round",[64,1148,1150],{"x":1137,"y":1149,"style":1138},"137.0","fast C parser",[1115,1152,1154],{"transform":1153},"translate(38.5,125.5) scale(0.625)",[1119,1155],{"d":1145,"fill":1122,"stroke":618,"style":1146},[64,1157,1159],{"x":1137,"y":1158,"style":1138},"163.0","must list every text-like column",[1115,1161,1163],{"transform":1162},"translate(38.5,151.5) scale(0.625)",[1119,1164],{"d":1165,"fill":1122,"stroke":610,"style":1146},"M6 6l12 12 M18 6L6 18",[64,1167,1169],{"x":1137,"y":1168,"style":1138},"189.0","a new column may be misparsed",[1115,1171,1173],{"transform":1172},"translate(38.5,177.5) scale(0.625)",[1119,1174],{"d":1165,"fill":1122,"stroke":610,"style":1146},[56,1176],{"x":1177,"y":1109,"width":1110,"height":1111,"rx":1112,"fill":617,"stroke":618,"style":660},"389.0",[1115,1179,1181],{"transform":1180},"translate(551.5,29.0) scale(1.083)",[1119,1182],{"d":1145,"fill":1122,"stroke":1183,"style":1124},"var(--teal-ink,#0b6157)",[64,1185,1188],{"x":1186,"y":1128,"style":1187},"564.5","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","dtype=str, then convert",[121,1190],{"x1":1191,"y1":1133,"x2":1192,"y2":1133,"stroke":618,"style":77},"405.0","724.0",[64,1194,1196],{"x":1195,"y":525,"style":1138},"429.0","nothing inferred",[1115,1198,1200],{"transform":1199},"translate(407.5,99.5) scale(0.625)",[1119,1201],{"d":1145,"fill":1122,"stroke":618,"style":1146},[64,1203,1204],{"x":1195,"y":1149,"style":1138},"each conversion visible",[1115,1206,1208],{"transform":1207},"translate(407.5,125.5) scale(0.625)",[1119,1209],{"d":1145,"fill":1122,"stroke":618,"style":1146},[64,1211,1212],{"x":1195,"y":1158,"style":1138},"new columns stay text",[1115,1214,1216],{"transform":1215},"translate(407.5,151.5) scale(0.625)",[1119,1217],{"d":1145,"fill":1122,"stroke":618,"style":1146},[64,1219,1220],{"x":1195,"y":1168,"style":1138},"a few more lines",[1115,1222,1224],{"transform":1223},"translate(407.5,177.5) scale(0.625)",[1119,1225],{"d":1226,"fill":1122,"stroke":1227,"style":1146},"M12 3.5l9.5 17h-19z M12 10v5 M12 17.8v.4","var(--gold,#b4740a)",[105,1229,1231],{"id":1230},"step-5-detect-the-separator-when-files-vary","Step 5 — Detect the separator when files vary",[10,1233,1234,1235,1238],{},"If exports arrive from several systems, some with commas and some with semicolons, detect the separator instead of hard-coding it. Python's ",[14,1236,1237],{},"csv.Sniffer"," looks at a sample and guesses:",[110,1240,1242],{"className":156,"code":1241,"language":158,"meta":115,"style":115},"import csv\n\nwith open(\"export.csv\", encoding=\"cp1252\", newline=\"\") as fh:\n    dialect = csv.Sniffer().sniff(fh.read(4096), delimiters=\";,\\t\")\nprint(repr(dialect.delimiter))\n\nsniffed = pd.read_csv(\"export.csv\", sep=dialect.delimiter, encoding=\"cp1252\", dtype=str)\nprint(sniffed.columns.tolist())\n",[14,1243,1244,1251,1255,1292,1323,1335,1339,1375],{"__ignoreMap":115},[119,1245,1246,1248],{"class":121,"line":122},[119,1247,166],{"class":165},[119,1249,1250],{"class":169}," csv\n",[119,1252,1253],{"class":121,"line":179},[119,1254,183],{"emptyLinePlaceholder":182},[119,1256,1257,1260,1263,1265,1267,1269,1271,1273,1275,1277,1280,1282,1284,1287,1289],{"class":121,"line":186},[119,1258,1259],{"class":165},"with",[119,1261,1262],{"class":213}," open",[119,1264,229],{"class":169},[119,1266,201],{"class":129},[119,1268,257],{"class":169},[119,1270,271],{"class":260},[119,1272,249],{"class":165},[119,1274,276],{"class":129},[119,1276,257],{"class":169},[119,1278,1279],{"class":260},"newline",[119,1281,249],{"class":165},[119,1283,935],{"class":129},[119,1285,1286],{"class":169},") ",[119,1288,173],{"class":165},[119,1290,1291],{"class":169}," fh:\n",[119,1293,1294,1297,1299,1302,1305,1308,1311,1313,1316,1319,1321],{"class":121,"line":195},[119,1295,1296],{"class":169},"    dialect ",[119,1298,249],{"class":165},[119,1300,1301],{"class":169}," csv.Sniffer().sniff(fh.read(",[119,1303,1304],{"class":213},"4096",[119,1306,1307],{"class":169},"), ",[119,1309,1310],{"class":260},"delimiters",[119,1312,249],{"class":165},[119,1314,1315],{"class":129},"\";,",[119,1317,1318],{"class":1060},"\\t",[119,1320,1057],{"class":129},[119,1322,204],{"class":169},[119,1324,1325,1327,1329,1332],{"class":121,"line":207},[119,1326,284],{"class":213},[119,1328,229],{"class":169},[119,1330,1331],{"class":213},"repr",[119,1333,1334],{"class":169},"(dialect.delimiter))\n",[119,1336,1337],{"class":121,"line":223},[119,1338,183],{"emptyLinePlaceholder":182},[119,1340,1341,1344,1346,1348,1350,1352,1354,1356,1359,1361,1363,1365,1367,1369,1371,1373],{"class":121,"line":238},[119,1342,1343],{"class":169},"sniffed ",[119,1345,249],{"class":165},[119,1347,252],{"class":169},[119,1349,201],{"class":129},[119,1351,257],{"class":169},[119,1353,261],{"class":260},[119,1355,249],{"class":165},[119,1357,1358],{"class":169},"dialect.delimiter, ",[119,1360,271],{"class":260},[119,1362,249],{"class":165},[119,1364,276],{"class":129},[119,1366,257],{"class":169},[119,1368,983],{"class":260},[119,1370,249],{"class":165},[119,1372,692],{"class":213},[119,1374,204],{"class":169},[119,1376,1377,1379],{"class":121,"line":243},[119,1378,284],{"class":213},[119,1380,1381],{"class":169},"(sniffed.columns.tolist())\n",[10,1383,1384,1385,1387,1388,1391],{},"Restricting ",[14,1386,1310],{}," to the realistic candidates makes the guess more reliable. ",[14,1389,1390],{},"pd.read_csv(sep=None, engine=\"python\")"," performs a similar sniff internally. Log the detected separator, so a wrong guess is visible when a file with an unusual first line comes through.",[105,1393,1395],{"id":1394},"step-6-write-the-result-to-excel","Step 6 — Write the result to Excel",[10,1397,1398],{},"Once the values are real numbers and dates, writing them to Excel is ordinary — and the reader's Excel displays them in the reader's locale, decimal commas included, because the workbook stores numbers, not text:",[110,1400,1402],{"className":156,"code":1401,"language":158,"meta":115,"style":115},"with pd.ExcelWriter(\"export.xlsx\", engine=\"openpyxl\") as writer:\n    df.to_excel(writer, index=False, sheet_name=\"Export\")\n    ws = writer.sheets[\"Export\"]\n    for (cell,) in ws.iter_rows(min_row=2, min_col=3, max_col=3):\n        cell.number_format = \"#,##0.00\"\n    for (cell,) in ws.iter_rows(min_row=2, min_col=2, max_col=2):\n        cell.number_format = \"DD.MM.YYYY\"\n\nback = pd.read_excel(\"export.xlsx\", dtype={\"PLZ\": str, \"Version\": str})\nprint(back.dtypes.to_dict())\n",[14,1403,1404,1431,1456,1471,1513,1523,1557,1566,1570,1607],{"__ignoreMap":115},[119,1405,1406,1408,1411,1414,1416,1419,1421,1424,1426,1428],{"class":121,"line":122},[119,1407,1259],{"class":165},[119,1409,1410],{"class":169}," pd.ExcelWriter(",[119,1412,1413],{"class":129},"\"export.xlsx\"",[119,1415,257],{"class":169},[119,1417,1418],{"class":260},"engine",[119,1420,249],{"class":165},[119,1422,1423],{"class":129},"\"openpyxl\"",[119,1425,1286],{"class":169},[119,1427,173],{"class":165},[119,1429,1430],{"class":169}," writer:\n",[119,1432,1433,1436,1439,1441,1444,1446,1449,1451,1454],{"class":121,"line":179},[119,1434,1435],{"class":169},"    df.to_excel(writer, ",[119,1437,1438],{"class":260},"index",[119,1440,249],{"class":165},[119,1442,1443],{"class":213},"False",[119,1445,257],{"class":169},[119,1447,1448],{"class":260},"sheet_name",[119,1450,249],{"class":165},[119,1452,1453],{"class":129},"\"Export\"",[119,1455,204],{"class":169},[119,1457,1458,1461,1463,1466,1468],{"class":121,"line":186},[119,1459,1460],{"class":169},"    ws ",[119,1462,249],{"class":165},[119,1464,1465],{"class":169}," writer.sheets[",[119,1467,1453],{"class":129},[119,1469,1470],{"class":169},"]\n",[119,1472,1473,1476,1479,1482,1485,1488,1490,1492,1494,1497,1499,1501,1503,1506,1508,1510],{"class":121,"line":195},[119,1474,1475],{"class":165},"    for",[119,1477,1478],{"class":169}," (cell,) ",[119,1480,1481],{"class":165},"in",[119,1483,1484],{"class":169}," ws.iter_rows(",[119,1486,1487],{"class":260},"min_row",[119,1489,249],{"class":165},[119,1491,526],{"class":213},[119,1493,257],{"class":169},[119,1495,1496],{"class":260},"min_col",[119,1498,249],{"class":165},[119,1500,547],{"class":213},[119,1502,257],{"class":169},[119,1504,1505],{"class":260},"max_col",[119,1507,249],{"class":165},[119,1509,547],{"class":213},[119,1511,1512],{"class":169},"):\n",[119,1514,1515,1518,1520],{"class":121,"line":207},[119,1516,1517],{"class":169},"        cell.number_format ",[119,1519,249],{"class":165},[119,1521,1522],{"class":129}," \"#,##0.00\"\n",[119,1524,1525,1527,1529,1531,1533,1535,1537,1539,1541,1543,1545,1547,1549,1551,1553,1555],{"class":121,"line":223},[119,1526,1475],{"class":165},[119,1528,1478],{"class":169},[119,1530,1481],{"class":165},[119,1532,1484],{"class":169},[119,1534,1487],{"class":260},[119,1536,249],{"class":165},[119,1538,526],{"class":213},[119,1540,257],{"class":169},[119,1542,1496],{"class":260},[119,1544,249],{"class":165},[119,1546,526],{"class":213},[119,1548,257],{"class":169},[119,1550,1505],{"class":260},[119,1552,249],{"class":165},[119,1554,526],{"class":213},[119,1556,1512],{"class":169},[119,1558,1559,1561,1563],{"class":121,"line":238},[119,1560,1517],{"class":169},[119,1562,249],{"class":165},[119,1564,1565],{"class":129}," \"DD.MM.YYYY\"\n",[119,1567,1568],{"class":121,"line":243},[119,1569,183],{"emptyLinePlaceholder":182},[119,1571,1572,1575,1577,1580,1582,1584,1586,1588,1590,1592,1594,1596,1598,1600,1602,1604],{"class":121,"line":281},[119,1573,1574],{"class":169},"back ",[119,1576,249],{"class":165},[119,1578,1579],{"class":169}," pd.read_excel(",[119,1581,1413],{"class":129},[119,1583,257],{"class":169},[119,1585,983],{"class":260},[119,1587,249],{"class":165},[119,1589,761],{"class":169},[119,1591,764],{"class":129},[119,1593,767],{"class":169},[119,1595,692],{"class":213},[119,1597,257],{"class":169},[119,1599,419],{"class":129},[119,1601,767],{"class":169},[119,1603,692],{"class":213},[119,1605,1606],{"class":169},"})\n",[119,1608,1609,1611],{"class":121,"line":290},[119,1610,284],{"class":213},[119,1612,1613],{"class":169},"(back.dtypes.to_dict())\n",[10,1615,1616,1619,1620,1622,1623,1626],{},[14,1617,1618],{},"#,##0.00"," is a locale-neutral code: a German Excel shows ",[14,1621,28],{},", an English one ",[14,1624,1625],{},"1,234.56",", from the same cell. That is the real advantage of converting CSV to a workbook — the file stops depending on the reader's regional settings.",[105,1628,1630],{"id":1629},"going-the-other-way-csv-for-european-excel-users","Going the other way: CSV for European Excel users",[10,1632,1633],{},"When a colleague in one of these locales needs a CSV rather than a workbook, write it in their conventions so Excel opens it straight into columns:",[110,1635,1637],{"className":156,"code":1636,"language":158,"meta":115,"style":115},"df.to_csv(\"for_excel_de.csv\", sep=\";\", decimal=\",\", index=False, encoding=\"utf-8-sig\", date_format=\"%d.%m.%Y\")\nprint(open(\"for_excel_de.csv\", encoding=\"utf-8-sig\").read().splitlines()[1])\n",[14,1638,1639,1695],{"__ignoreMap":115},[119,1640,1641,1644,1647,1649,1651,1653,1655,1657,1659,1661,1663,1665,1667,1669,1671,1673,1675,1677,1680,1682,1685,1687,1689,1691,1693],{"class":121,"line":122},[119,1642,1643],{"class":169},"df.to_csv(",[119,1645,1646],{"class":129},"\"for_excel_de.csv\"",[119,1648,257],{"class":169},[119,1650,261],{"class":260},[119,1652,249],{"class":165},[119,1654,266],{"class":129},[119,1656,257],{"class":169},[119,1658,375],{"class":260},[119,1660,249],{"class":165},[119,1662,380],{"class":129},[119,1664,257],{"class":169},[119,1666,1438],{"class":260},[119,1668,249],{"class":165},[119,1670,1443],{"class":213},[119,1672,257],{"class":169},[119,1674,271],{"class":260},[119,1676,249],{"class":165},[119,1678,1679],{"class":129},"\"utf-8-sig\"",[119,1681,257],{"class":169},[119,1683,1684],{"class":260},"date_format",[119,1686,249],{"class":165},[119,1688,1057],{"class":129},[119,1690,1061],{"class":1060},[119,1692,1064],{"class":129},[119,1694,204],{"class":169},[119,1696,1697,1699,1701,1704,1706,1708,1710,1712,1714,1716,1719,1721],{"class":121,"line":179},[119,1698,284],{"class":213},[119,1700,229],{"class":169},[119,1702,1703],{"class":213},"open",[119,1705,229],{"class":169},[119,1707,1646],{"class":129},[119,1709,257],{"class":169},[119,1711,271],{"class":260},[119,1713,249],{"class":165},[119,1715,1679],{"class":129},[119,1717,1718],{"class":169},").read().splitlines()[",[119,1720,506],{"class":213},[119,1722,1723],{"class":169},"])\n",[10,1725,1726,1729,1730,1734],{},[14,1727,1728],{},"utf-8-sig"," writes the byte-order mark that tells Excel the file is UTF-8, so umlauts survive; without it, Excel may decode the file as the local code page. ",[31,1731,1733],{"href":1732},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fconvert-excel-to-csv-with-python\u002F","Convert Excel to CSV with Python"," covers encodings and quoting for CSV output in general.",[105,1736,1738],{"id":1737},"common-pitfalls","Common pitfalls",[1740,1741,1742,1752,1765,1777],"ul",{},[1743,1744,1745,1751],"li",{},[1746,1747,1748,1750],"strong",{},[14,1749,330],{}," without protecting other columns."," Dates, versions and dotted codes are turned into integers without warning.",[1743,1753,1754,1757,1758,1760,1761,1764],{},[1746,1755,1756],{},"Guessing the encoding."," Try ",[14,1759,1728],{}," first, then ",[14,1762,1763],{},"cp1252","; a decode error names the byte that failed.",[1743,1766,1767,1770,1771,1773,1774,1776],{},[1746,1768,1769],{},"Month-first dates."," ",[14,1772,552],{}," is 1 February in these locales; pass ",[14,1775,845],{}," or an explicit format.",[1743,1778,1779,1782,1783,1785,1786,1788],{},[1746,1780,1781],{},"Postcodes as numbers."," Give them a ",[14,1784,692],{}," dtype or ",[14,1787,316],{}," becomes 1067.",[105,1790,1792],{"id":1791},"performance-notes","Performance notes",[10,1794,1795,1796,257,1798,444,1800,1802,1803,1806,1807,1809,1810,1813],{},"The C parser handles ",[14,1797,261],{},[14,1799,375],{},[14,1801,385],{}," natively and reads millions of rows quickly. ",[14,1804,1805],{},"sep=None"," switches to the slower Python engine, so sniff once with ",[14,1808,1237],{}," and pass the result. Converting columns with ",[14,1811,1812],{},"map"," is slower than the built-in options on very large files, but only the amount columns need it, and correctness is worth the seconds.",[105,1815,1817],{"id":1816},"conclusion","Conclusion",[10,1819,1820,1821,1823,1824,1826,1827,1829,1830,1307,1832,1834,1835,1837,1838,1840,1841,1844,1845,257,1847,444,1849,1851],{},"European CSV exports need ",[14,1822,304],{},", the right ",[14,1825,271],{}," (",[14,1828,1763],{}," or ",[14,1831,1728],{},[14,1833,326],{}," and usually ",[14,1836,330],{},". Because ",[14,1839,385],{}," applies to every column pandas treats as numeric, protect text-like columns with ",[14,1842,1843],{},"dtype=str"," and parse dates in the same call — or read everything as text and convert the amount columns yourself. Write the result to Excel with locale-neutral number formats, and write CSV back with ",[14,1846,304],{},[14,1848,326],{},[14,1850,1728],{}," for readers who need it.",[105,1853,1855],{"id":1854},"frequently-asked-questions","Frequently asked questions",[10,1857,1858,1861],{},[1746,1859,1860],{},"Why do European CSV files use semicolons?","\nIn locales where the comma is the decimal separator, a comma cannot also separate fields without quoting every number. Excel in those locales saves CSV with semicolons instead, and many ERP systems follow the same convention.",[10,1863,1864,1867],{},[1746,1865,1866],{},"Is it safe to pass thousands=\".\" to read_csv?","\nOnly for columns that really are numbers. pandas applies it to every column it tries to parse as numeric, so dates such as 31.01.2026 become 31012026 and version strings such as 2.0 become 20. Give text-like columns a str dtype, or convert amount columns yourself.",[10,1869,1870,1873],{},[1746,1871,1872],{},"What encoding do these files use?","\nUsually Windows-1252 (cp1252) when saved by older Excel versions or ERP exports, or UTF-8 with a byte-order mark (utf-8-sig) when saved as \"CSV UTF-8\". A UnicodeDecodeError on the first umlaut means you guessed wrong.",[10,1875,1876,1879],{},[1746,1877,1878],{},"How do I write a CSV that European Excel opens correctly?","\nUse to_csv with sep=\";\", decimal=\",\" and encoding=\"utf-8-sig\". The byte-order mark tells Excel the file is UTF-8, and the semicolons and decimal commas match what Excel expects in those locales.",[105,1881,1883],{"id":1882},"related","Related",[1740,1885,1886,1892,1899,1906],{},[1743,1887,1888,1889,1891],{},"Up one level: ",[31,1890,34],{"href":33}," — every format and conversion.",[1743,1893,1894,1898],{},[31,1895,1897],{"href":1896},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fconvert-csv-files-to-excel-with-python\u002F","Convert CSV Files to Excel with Python"," — the general CSV-to-workbook pipeline.",[1743,1900,1901,1905],{},[31,1902,1904],{"href":1903},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Fparse-excel-dates-into-python-datetimes-with-pandas\u002F","Parse Excel Dates into Python datetimes with pandas"," — date formats in depth.",[1743,1907,1908,1912],{},[31,1909,1911],{"href":1910},"\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fkeep-leading-zeros-when-reading-excel-with-pandas\u002F","Keep Leading Zeros When Reading Excel with pandas"," — the same dtype rule for workbooks.",[1914,1915,1916],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}",{"title":115,"searchDepth":179,"depth":179,"links":1918},[1919,1920,1921,1922,1923,1924,1925,1926,1927,1928,1929,1930,1931],{"id":107,"depth":179,"text":108},{"id":149,"depth":179,"text":150},{"id":320,"depth":179,"text":321},{"id":680,"depth":179,"text":681},{"id":856,"depth":179,"text":857},{"id":1230,"depth":179,"text":1231},{"id":1394,"depth":179,"text":1395},{"id":1629,"depth":179,"text":1630},{"id":1737,"depth":179,"text":1738},{"id":1791,"depth":179,"text":1792},{"id":1816,"depth":179,"text":1817},{"id":1854,"depth":179,"text":1855},{"id":1882,"depth":179,"text":1883},"2026-09-25","CSV files saved by European Excel and ERP systems use semicolons between fields, commas as decimal points, dots as thousands separators and a Windows code page. Read them into pandas correctly — and avoid the thousands option silently turning dates like 31.01.2026 into the number 31012026.","md",[1936,1938,1940,1942],{"q":1860,"a":1937},"In locales where the comma is the decimal separator, a comma cannot also separate fields without quoting every number. Excel in those locales saves CSV with semicolons instead, and many ERP systems follow the same convention.",{"q":1866,"a":1939},"Only for columns that really are numbers. pandas applies it to every column it tries to parse as numeric, so dates such as 31.01.2026 become 31012026 and version strings such as 2.0 become 20. Give text-like columns a str dtype, or convert amount columns yourself.",{"q":1872,"a":1941},"Usually Windows-1252 (cp1252) when saved by older Excel versions or ERP exports, or UTF-8 with a byte-order mark (utf-8-sig) when saved as \"CSV UTF-8\". A UnicodeDecodeError on the first umlaut means you guessed wrong.",{"q":1878,"a":1943},"Use to_csv with sep=\";\", decimal=\",\" and encoding=\"utf-8-sig\". The byte-order mark tells Excel the file is UTF-8, and the semicolons and decimal commas match what Excel expects in those locales.","read a csv with semicolons and decimal commas",null,{"breadcrumb":1947},[1948,1951,1954],{"name":1949,"item":1950},"Home","\u002F",{"name":1952,"item":1953},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":34,"item":33},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-csv-exports-with-semicolons-and-decimal-commas",{"title":1957,"description":1958},"pandas read_csv with sep=\";\" and decimal=\",\": European CSV Files","Read semicolon-separated CSV with decimal commas in pandas: sep, decimal, thousands, cp1252 and utf-8-sig encodings, day-first dates, sniffing the delimiter, and why thousands=\".\" corrupts dates and version numbers.","read-csv-exports-with-semicolons-and-decimal-commas","getting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-csv-exports-with-semicolons-and-decimal-commas\u002Findex",[1962,1963,1964],"file-formats","reading-excel","data-cleaning","how-to","1OjNtrrcUpQuCXTnl3lsWrsmqqTM2VcafP87jX1PL44",[1968,1972],{"title":1969,"path":1970,"stem":1971,"children":-1},"Read Apple Numbers Files in Python","\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-apple-numbers-files-in-python","getting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-apple-numbers-files-in-python\u002Findex",{"title":1973,"path":1974,"stem":1975,"children":-1},"Read Excel 2003 XML Spreadsheet Files","\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-excel-2003-xml-spreadsheet-files","getting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-excel-2003-xml-spreadsheet-files\u002Findex",[1977,1979],{"path":1978,"title":34},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions",{"path":1980,"title":1952},"\u002Fgetting-started-with-python-excel-automation",[],[1983,1986,1988,1992,1995,1999],{"path":1984,"title":1985,"kind":1965},"\u002Fadvanced-data-transformation-and-cleaning\u002Freading-excel-with-polars-and-arrow\u002Fconvert-excel-files-to-parquet-with-python","Convert Excel Files to Parquet with Python",{"path":1987,"title":1733,"kind":1965},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fconvert-excel-to-csv-with-python",{"path":1989,"title":1990,"kind":1991},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fexcel-vs-csv-vs-parquet-for-python-data-pipelines","Excel vs CSV vs Parquet for Python Data Pipelines","compare",{"path":1993,"title":1994,"kind":1991},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpick-an-excel-engine-for-xlsx-xlsm-xls-xlsb-and-ods","Pick an Excel Engine for .xlsx, .xlsm, .xls, .xlsb and .ods",{"path":1996,"title":1997,"kind":1998},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Ffix-garbled-characters-in-excel-data","Fix Garbled Characters Like \"CafÃ©\" and \"â€™\" in Excel Data","fix",{"path":2000,"title":2001,"kind":1965},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Fextract-vba-macro-code-from-an-excel-file","Extract VBA Macro Code from an Excel File",1790972064498]