OpenCsv CsvRequiredFieldEmptyException: Number of data fields does not match number of headers.

本文探讨了在使用OpenCsv处理csv文件时遇到的CsvRequiredFieldEmptyException异常,详细解析了问题原因在于数据字段数量与表头不匹配,并提供了解决方案。

OpenCsv

在使用OpenCsv的过程中遇到错误:CsvRequiredFieldEmptyException: Number of data fields does not match number of headers.

csv文件格式如下:
在这里插入图片描述

--- [2025-10-14 10:26:47] 开始同步 --- 🔁 上次同步时间: ⚠️ 时间解析失败,使用默认方式: time data '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' does not match format '%Y-%m-%d %H:%M:%S' 🟢 无新数据,跳过上传 💤 等待 30 秒... # sync_client.py import json import requests import sqlite3 import time import os import socket from datetime import datetime, timedelta import sys import io if sys.stdout.encoding.lower() != 'utf-8': sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') # ...existing code... # ======================== # IP 与 LineNumber 映射表(支持自动识别) # ======================== SORTERS = { '192.168.110.56': 1, '192.168.110.60': 2, '192.168.110.12': 3, '192.168.110.105': 4, '192.168.110.77': 5, '192.168.110.52': 6, '192.168.110.44': 7, '192.168.110.22': 8, '192.168.110.53': 9, '192.168.110.92': 0 # 👈 新增:0号线为服务端或测试机 } # ======================== # 获取本机 IP 地址 # ======================== def get_local_ip(): try: with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s: s.connect(("8.8.8.8", 80)) ip = s.getsockname()[0] if ip.startswith("192.168.") or ip.startswith("10.") or ip.startswith("172."): return ip else: print(f"⚠️ 获取到非内网IP: {ip},使用备用地址") return "127.0.0.1" except Exception: return "127.0.0.1" # ======================== # 加载配置文件(自动根据 IP 推导 LineNumber) # ======================== def load_config(): try: with open('config.json', 'r', encoding='utf-8') as f: config = json.load(f) # 验证必要字段 required_fields = ['db_path', 'server_url'] for field in required_fields: if field not in config: raise KeyError(f"配置文件缺少 '{field}'") # 设置默认值 config.setdefault('last_sync_time_file', 'last_sync.txt') config.setdefault('sync_interval', 30) # 获取本机 IP local_ip = get_local_ip() print(f"📍 客户端本地IP: {local_ip}") # 查找对应的 LineNumber if local_ip not in SORTERS: print(f"❌ 错误:当前IP ({local_ip}) 未在 SORTERS 映射中定义!") print(f"💡 支持的设备IP与线号:{dict(sorted(SORTERS.items()))}") exit(1) LineNumber = SORTERS[local_ip] device_id = str(LineNumber) # 兼容性:用线号作为设备ID # 注入自动识别的信息 config['LineNumber'] = LineNumber config['device_id'] = device_id config['local_ip'] = local_ip return config except FileNotFoundError: print("❌ 错误:找不到 config.json 配置文件") exit(1) except json.JSONDecodeError as e: print(f"❌ 配置文件格式错误:{e}") exit(1) except Exception as e: print(f"❌ 加载配置失败: {e}") exit(1) # ======================== # 读取上次同步时间 # ======================== def read_last_sync_time(filename): if os.path.exists(filename): try: with open(filename, 'r') as f: t = f.read().strip() print(f"🔁 上次同步时间: {t}") return t except Exception as e: print(f"⚠️ 读取上次同步时间失败: {e}") print("🆕 首次运行,将同步全部数据") return None # ======================== # 保存本次同步时间 # ======================== def save_last_sync_time(filename, dt_str): try: with open(filename, 'w') as f: f.write(dt_str) except Exception as e: print(f"⚠️ 保存同步时间失败: {e}") # ======================== # 从 SQLite 读取变更数据(增量 + 注入 LineNumber) # ======================== def read_changes_from_sqlite(db_path, since_time=None, LineNumber=1): """ 读取自 since_time 以来发生变化的数据,并注入 LineNumber 字段 """ conn = None try: conn = sqlite3.connect(db_path) conn.row_factory = sqlite3.Row # 支持按列名访问 cursor = conn.cursor() # 要同步的表列表 tables = [ 'appsortingtasks', 'appsortingtaskitems', 'appsortingtaskskus', 'appsortingtaskboxes', 'appsortingtaskimages', 'appreportrecords', 'appoperationlogs' ] changes = {} for table in tables: try: if since_time: cursor.execute( f"SELECT * FROM `{table}` WHERE creationtime >= ?", (since_time,) ) else: default_start = "2025-09-20 00:00:00" cursor.execute( f"SELECT * FROM `{table}` WHERE creationtime >= ?", (default_start,) ) rows = cursor.fetchall() if not rows: changes[table] = [] continue # 转换为字典并注入 LineNumber result = [] columns = [desc[0] for desc in cursor.description] for row in rows: item = dict(zip(columns, row)) item["LineNumber"] = LineNumber # ⭐ 注入字段 result.append(item) changes[table] = result start_str = since_time or "2025-09-20 00:00:00" print(f"📊 发现 {len(result)} 条 [{table}] 新数据 (creationtime >= {start_str})") except sqlite3.OperationalError as e: if "no such column" in str(e): print(f"❌ 表 {table} 缺少 creationtime 或字段名拼写错误: {e}") elif "no such table" in str(e): print(f"❌ 表不存在: {table} —— 检查数据库是否正确") else: print(f"⚠️ SQL错误读取 {table}: {e}") changes[table] = [] except Exception as e: print(f"⚠️ 未知错误读取 {table}: {e}") changes[table] = [] return changes except Exception as e: print(f"❌ 连接数据库失败: {e}") return {} finally: if conn: conn.close() # ======================== # 发送数据到服务器(带 X-Real-IP 头) # ======================== def send_to_server(data, server_url, device_id, LineNumber, client_ip): payload = { "device_id": device_id, "LineNumber": LineNumber, "ip": client_ip, "timestamp": datetime.now().isoformat(), "records": data } headers = { 'Content-Type': 'application/json', 'X-Real-IP': client_ip # ✅ 让后端获取真实客户端IP } try: print(f"📤 正在发送请求到: {server_url}") response = requests.post( server_url, json=payload, headers=headers, timeout=60 ) if response.status_code == 200: try: result = response.json() print(f"✅ 成功接收!响应:") print(json.dumps(result, ensure_ascii=False, indent=2)) return True except json.JSONDecodeError: print(f"✅ 同步成功,但返回非JSON: {response.text.strip()}") return True else: try: error_data = response.json() error_msg = error_data.get("error", "未知错误") details = error_data.get("details", []) print(f"❌ 请求失败 [{response.status_code}]: {error_msg}") for detail in details: print(f" → {detail}") except json.JSONDecodeError: text = response.text.strip() print(f"❌ 请求失败 [{response.status_code}]: {text}") return False except requests.exceptions.ConnectionError: print("🔴 网络连接错误:无法连接到服务器") except requests.exceptions.Timeout: print("⏰ 请求超时:服务器无响应") except requests.exceptions.RequestException as e: print(f"📡 其他网络异常: {e}") return False # ======================== # 主循环逻辑 # ======================== def main(): try: config = load_config() server_url = config['server_url'] db_path = config['db_path'] interval = config['sync_interval'] device_id = config['device_id'] LineNumber = config['LineNumber'] last_sync_file = config['last_sync_time_file'] print(f"🚀 客户端启动 | AI分播机编号: {device_id} (由IP自动识别) | 分播线编号: {LineNumber}") print(f"📍 服务地址: {server_url}") print(f"🔄 同步间隔: {interval} 秒") TIME_FORMAT = "%Y-%m-%d %H:%M:%S" while True: print(f"\n--- [{datetime.now().strftime(TIME_FORMAT)}] 开始同步 ---") # 读取上次同步时间 last_time_str = read_last_sync_time(last_sync_file) # 计算查询起始时间(含3小时前缓冲) query_start_time = None if last_time_str: try: last_time_dt = datetime.strptime(last_time_str, TIME_FORMAT) query_start_dt = last_time_dt - timedelta(hours=3) query_start_time = query_start_dt.strftime(TIME_FORMAT) print(f"⏳ 使用增量同步(含3小时前缓冲): creationtime >= {query_start_time}") except ValueError as e: print(f"⚠️ 时间解析失败,使用默认方式: {e}") query_start_time = None else: print("🆕 首次运行,使用默认起始时间") query_start_time = "2025-09-20 00:00:00" # 读取数据库变更 changes = read_changes_from_sqlite(db_path, since_time=query_start_time, LineNumber=LineNumber) # 统计是否有新数据 has_changes = any(len(records) > 0 for records in changes.values()) total_rows = sum(len(v) for v in changes.values()) if not has_changes: print("🟢 无新数据,跳过上传") else: success = send_to_server(changes, server_url, device_id, LineNumber, config['local_ip']) if success: current_time = datetime.now().strftime(TIME_FORMAT) save_last_sync_time(last_sync_file, current_time) print(f"💾 已更新同步时间: {current_time}") else: print("🟡 同步失败,将在下一轮重试...") print(f"💤 等待 {interval} 秒...") time.sleep(interval) except Exception as e: print(f"❌ 主程序异常: {e}") import traceback traceback.print_exc() if __name__ == '__main__': main()
最新发布
10-15
#include <iostream> #include <fstream> #include <vector> #include <map> #include <algorithm> #include <functional> #include <cctype> #include <memory> #include <variant> #include <regex> #include <cmath> #include <unordered_map> #include <locale> #include <iomanip> #include <sstream> #ifdef _WIN32 #include <windows.h> #include <io.h> #include <fcntl.h> #endif // 基础类型定义 using Value = std::variant<int, double, std::string, bool>; using Row = std::vector<Value>; using Table = std::vector<Row>; using ColumnMap = std::unordered_map<std::string, int>; // 字符串处理函数 std::string trim(const std::string& str) { // 去除字符串两端的空白字符 size_t first = str.find_first_not_of(" \t\n\r\f\v"); if (first == std::string::npos) return ""; size_t last = str.find_last_not_of(" \t\n\r\f\v"); return str.substr(first, (last - first + 1)); } // 改进的DBF编码转换函数 std::string convertDBFToUTF8(const std::string& str) { #ifdef _WIN32 if (str.empty()) return str; // 尝试从GBK转换为UTF-8 int len = MultiByteToWideChar(936, 0, str.c_str(), -1, NULL, 0); if (len > 0) { wchar_t* wstr = new wchar_t[len]; MultiByteToWideChar(936, 0, str.c_str(), -1, wstr, len); len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL); if (len > 0) { char* utf8Str = new char[len]; WideCharToMultiByte(CP_UTF8, 0, wstr, -1, utf8Str, len, NULL, NULL); std::string result(utf8Str); delete[] wstr; delete[] utf8Str; return result; } delete[] wstr; } return str; #else return str; // 非Windows平台直接返回 #endif } // 用于处理DBF字段名的函数 std::string processDBFFieldName(const char* name, size_t len) { std::string fieldName(name, len); fieldName = fieldName.substr(0, fieldName.find('\0')); fieldName = trim(fieldName); fieldName = convertDBFToUTF8(fieldName); return fieldName; } // 用于处理DBF字符串字段的函数 std::string processDBFString(const char* str, size_t len) { std::string result(str, len); result = trim(result); result = convertDBFToUTF8(result); return result; } struct FieldDescriptor { char name[11]; char type; uint32_t address; uint8_t length; uint8_t decimal; }; struct DBFHeader { uint8_t version; uint8_t last_update[3]; uint32_t num_records; uint16_t header_size; uint16_t record_size; }; // DBF 文件读取器 class DBFReader { public: DBFReader(const std::string& filename) : filename(filename) {} bool read() { std::ifstream file(filename, std::ios::binary); if (!file) return false; // 读取文件头 file.read(reinterpret_cast<char*>(&header), sizeof(DBFHeader)); // 读取字段描述 int num_fields = (header.header_size - sizeof(DBFHeader) - 1) / 32; for (int i = 0; i < num_fields; ++i) { FieldDescriptor fd; file.read(reinterpret_cast<char*>(&fd), sizeof(FieldDescriptor)); fields.push_back(fd); // 处理字段名 std::string fieldName = processDBFFieldName(fd.name, sizeof(fd.name)); columnMap[fieldName] = i; } // 跳过文件头结束符 file.seekg(header.header_size); // 读取记录 for (int i = 0; i < header.num_records; ++i) { char delete_flag; file.read(&delete_flag, 1); if (delete_flag == '*') { file.seekg(header.record_size - 1, std::ios::cur); continue; // 跳过已删除记录 } Row row; for (size_t j = 0; j < fields.size(); j++) { const auto& field = fields[j]; char* buffer = new char[field.length + 1]; file.read(buffer, field.length); buffer[field.length] = '\0'; // 转换数据类型 Value value; switch (field.type) { case 'N': // 数值 case 'I': // 整数 case 'F': // 浮点数 { std::string numStr = trim(std::string(buffer)); if (numStr.empty()) { if (field.type == 'I') { value = 0; // 整数默认值 } else { value = 0.0; // 浮点数默认值 } } else if (field.decimal > 0 || numStr.find('.') != std::string::npos) { try { value = std::stod(numStr); } catch (...) { value = 0.0; } } else { try { value = std::stoi(numStr); } catch (...) { value = 0; } } } break; case 'D': // 日期 (格式: YYYYMMDD) { std::string dateStr(buffer); if (dateStr.length() == 8 && std::all_of(dateStr.begin(), dateStr.end(), ::isdigit)) { value = dateStr.substr(0, 4) + "-" + dateStr.substr(4, 2) + "-" + dateStr.substr(6, 2); } else { value = dateStr; } } break; case 'L': // 逻辑 { char c = std::toupper(buffer[0]); value = (c == 'T' || c == 'Y' || c == '1'); } break; case 'C': // 字符 case 'M': // 备注 default: { // 处理字符串字段 std::string str = processDBFString(buffer, field.length); value = str; } break; } row.push_back(value); delete[] buffer; } data.push_back(row); } return true; } const Table& getData() const { return data; } const std::vector<FieldDescriptor>& getFields() const { return fields; } const ColumnMap& getColumnMap() const { return columnMap; } private: std::string parseDate(const char* buffer) { // 简单日期转换: YYYYMMDD -> YYYY-MM-DD std::string date(buffer); if (date.length() == 8 && std::all_of(date.begin(), date.end(), ::isdigit)) { return date.substr(0, 4) + "-" + date.substr(4, 2) + "-" + date.substr(6, 2); } return date; } std::string filename; DBFHeader header; std::vector<FieldDescriptor> fields; Table data; ColumnMap columnMap; }; // SQL 查询解析器 class SQLParser { public: enum TokenType { SELECT, FROM, WHERE, INSERT, INTO, VALUES, UPDATE, SET, DELETE_TOKEN, AND, OR, NOT, EQ, NEQ, LT, GT, LTE, GTE, COMMA, LPAREN, RPAREN, IDENTIFIER, STRING, NUMBER, BOOL, STAR, EOF_TOKEN }; struct Token { TokenType type; std::string value; // 添加构造函数以支持初始化列表 Token(TokenType t) : type(t) {} Token(TokenType t, const std::string& v) : type(t), value(v) {} }; explicit SQLParser(const std::string& sql) : sql(sql), pos(0) {} std::vector<Token> tokenize() { std::vector<Token> tokens; while (pos < sql.size()) { char c = sql[pos]; if (std::isspace(c)) { pos++; continue; } // 处理标识符和关键字 if (std::isalpha(c) || c == '_') { std::string ident; while (pos < sql.size() && (std::isalnum(sql[pos]) || sql[pos] == '_')) { ident += sql[pos++]; } std::transform(ident.begin(), ident.end(), ident.begin(), ::toupper); if (ident == "SELECT") tokens.push_back(Token(SELECT)); else if (ident == "FROM") tokens.push_back(Token(FROM)); else if (ident == "WHERE") tokens.push_back(Token(WHERE)); else if (ident == "INSERT") tokens.push_back(Token(INSERT)); else if (ident == "INTO") tokens.push_back(Token(INTO)); else if (ident == "VALUES") tokens.push_back(Token(VALUES)); else if (ident == "UPDATE") tokens.push_back(Token(UPDATE)); else if (ident == "SET") tokens.push_back(Token(SET)); else if (ident == "DELETE") tokens.push_back(Token(DELETE_TOKEN)); else if (ident == "AND") tokens.push_back(Token(AND)); else if (ident == "OR") tokens.push_back(Token(OR)); else if (ident == "NOT") tokens.push_back(Token(NOT)); else if (ident == "TRUE" || ident == "FALSE") { tokens.push_back(Token(BOOL, ident)); } else { tokens.push_back(Token(IDENTIFIER, ident)); } continue; } // 处理数字 if (std::isdigit(c) || c == '.') { std::string num; bool hasDecimal = false; while (pos < sql.size() && (std::isdigit(sql[pos]) || sql[pos] == '.')) { if (sql[pos] == '.') { if (hasDecimal) break; hasDecimal = true; } num += sql[pos++]; } tokens.push_back(Token(NUMBER, num)); continue; } // 处理字符串 if (c == '\'') { pos++; std::string str; while (pos < sql.size() && sql[pos] != '\'') { if (sql[pos] == '\\' && pos + 1 < sql.size()) { pos++; switch (sql[pos]) { case 'n': str += '\n'; break; case 't': str += '\t'; break; case 'r': str += '\r'; break; default: str += sql[pos]; break; } } else { str += sql[pos]; } pos++; } if (pos < sql.size() && sql[pos] == '\'') pos++; tokens.push_back(Token(STRING, str)); continue; } // 处理操作符 switch (c) { case '*': tokens.push_back(Token(STAR)); break; case ',': tokens.push_back(Token(COMMA)); break; case '(': tokens.push_back(Token(LPAREN)); break; case ')': tokens.push_back(Token(RPAREN)); break; case '=': tokens.push_back(Token(EQ)); break; case '<': if (pos + 1 < sql.size() && sql[pos + 1] == '>') { tokens.push_back(Token(NEQ)); pos++; } else if (pos + 1 < sql.size() && sql[pos + 1] == '=') { tokens.push_back(Token(LTE)); pos++; } else { tokens.push_back(Token(LT)); } break; case '>': if (pos + 1 < sql.size() && sql[pos + 1] == '=') { tokens.push_back(Token(GTE)); pos++; } else { tokens.push_back(Token(GT)); } break; case '!': if (pos + 1 < sql.size() && sql[pos + 1] == '=') { tokens.push_back(Token(NEQ)); pos++; } break; } pos++; } tokens.push_back(Token(EOF_TOKEN)); return tokens; } private: std::string sql; size_t pos; }; // SQL 查询执行引擎 class SQLExecutor { public: SQLExecutor(DBFReader& reader) : reader(reader) {} Table execute(const std::string& sql) { SQLParser parser(sql); auto tokens = parser.tokenize(); if (tokens.empty()) return {}; switch (tokens[0].type) { case SQLParser::SELECT: return executeSelect(tokens); case SQLParser::INSERT: return executeInsert(tokens); case SQLParser::UPDATE: return executeUpdate(tokens); case SQLParser::DELETE_TOKEN: return executeDelete(tokens); default: std::cerr << "Unsupported SQL statement" << std::endl; return {}; } } private: // SELECT 查询执行 Table executeSelect(const std::vector<SQLParser::Token>& tokens) { Table result; auto& data = reader.getData(); auto& fields = reader.getFields(); auto& columnMap = reader.getColumnMap(); // 解析字段列表 std::vector<std::string> selectedColumns; size_t pos = 1; // 跳过SELECT if (pos < tokens.size() && tokens[pos].type == SQLParser::STAR) { for (const auto& field : fields) { std::string fieldName = processDBFFieldName(field.name, sizeof(field.name)); selectedColumns.push_back(fieldName); } pos += 2; // 跳过 * 和 FROM } else { while (pos < tokens.size() && tokens[pos].type != SQLParser::FROM) { if (tokens[pos].type == SQLParser::IDENTIFIER) { selectedColumns.push_back(tokens[pos].value); } pos++; } if (pos < tokens.size()) pos++; // 跳过FROM } // 解析表名 (忽略,因为只有一个表) if (pos < tokens.size() && tokens[pos].type == SQLParser::IDENTIFIER) { std::string tableName = tokens[pos].value; pos++; } // 解析WHERE条件 std::function<bool(const Row&)> condition = [](const Row&) { return true; }; if (pos < tokens.size() && tokens[pos].type == SQLParser::WHERE) { pos++; condition = parseCondition(tokens, pos, columnMap); } // 执行查询 for (const auto& row : data) { if (condition(row)) { Row resultRow; for (const auto& col : selectedColumns) { // 尝试直接匹配 auto it = columnMap.find(col); if (it != columnMap.end()) { resultRow.push_back(row[it->second]); } else { // 如果直接匹配失败,尝试其他可能的编码格式 bool found = false; for (const auto& pair : columnMap) { // 比较去除空格后的字段名 std::string mapKey = pair.first; std::string reqKey = col; // 去除空格 mapKey.erase(std::remove(mapKey.begin(), mapKey.end(), ' '), mapKey.end()); reqKey.erase(std::remove(reqKey.begin(), reqKey.end(), ' '), reqKey.end()); if (mapKey == reqKey) { resultRow.push_back(row[pair.second]); found = true; break; } } if (!found) { resultRow.push_back(std::string("")); } } } result.push_back(resultRow); } } return result; } // INSERT 语句执行 Table executeInsert(const std::vector<SQLParser::Token>& tokens) { size_t pos = 1; // 跳过INSERT if (tokens[pos].type != SQLParser::INTO) { std::cerr << "Expected INTO after INSERT" << std::endl; return {}; } pos++; // 跳过INTO if (tokens[pos].type != SQLParser::IDENTIFIER) { std::cerr << "Expected table name after INTO" << std::endl; return {}; } std::string tableName = tokens[pos++].value; if (tokens[pos].type != SQLParser::LPAREN) { std::cerr << "Expected '(' after table name" << std::endl; return {}; } pos++; // 跳过'(' // 解析列名 std::vector<std::string> columns; while (pos < tokens.size() && tokens[pos].type != SQLParser::RPAREN) { if (tokens[pos].type == SQLParser::IDENTIFIER) { columns.push_back(tokens[pos].value); } pos++; if (tokens[pos].type == SQLParser::COMMA) pos++; } pos++; // 跳过')' if (tokens[pos].type != SQLParser::VALUES) { std::cerr << "Expected VALUES after column list" << std::endl; return {}; } pos++; // 跳过VALUES if (tokens[pos].type != SQLParser::LPAREN) { std::cerr << "Expected '(' after VALUES" << std::endl; return {}; } pos++; // 跳过'(' // 解析值 Row newRow; while (pos < tokens.size() && tokens[pos].type != SQLParser::RPAREN) { if (tokens[pos].type == SQLParser::STRING) { newRow.push_back(tokens[pos].value); } else if (tokens[pos].type == SQLParser::NUMBER) { if (tokens[pos].value.find('.') != std::string::npos) { newRow.push_back(std::stod(tokens[pos].value)); } else { newRow.push_back(std::stoi(tokens[pos].value)); } } else if (tokens[pos].type == SQLParser::BOOL) { newRow.push_back((tokens[pos].value == "TRUE")); } else if (tokens[pos].type == SQLParser::IDENTIFIER) { // 假设为列名或其他不支持的类型,暂不处理 newRow.push_back(tokens[pos].value); } pos++; if (tokens[pos].type == SQLParser::COMMA) pos++; } pos++; // 跳过')' // 验证列数与值数一致 if (columns.size() != newRow.size()) { std::cerr << "Column count does not match value count" << std::endl; return {}; } // 获取列映射 const auto& columnMap = reader.getColumnMap(); const auto& fields = reader.getFields(); // 构造完整行数据,按字段顺序填充 Row fullRow(fields.size()); for (size_t i = 0; i < columns.size(); ++i) { auto it = columnMap.find(columns[i]); if (it == columnMap.end()) { std::cerr << "Unknown column: " << columns[i] << std::endl; return {}; } fullRow[it->second] = newRow[i]; } // 添加新行到数据表 const_cast<Table&>(reader.getData()).push_back(fullRow); // 返回新插入的行 Table result; result.push_back(fullRow); return result; } // UPDATE 语句执行 Table executeUpdate(const std::vector<SQLParser::Token>& tokens) { size_t pos = 1; // 跳过UPDATE if (tokens[pos].type != SQLParser::IDENTIFIER) { std::cerr << "Expected table name after UPDATE" << std::endl; return {}; } std::string tableName = tokens[pos++].value; if (tokens[pos].type != SQLParser::SET) { std::cerr << "Expected SET after table name in UPDATE" << std::endl; return {}; } pos++; // 跳过SET // 解析更新字段 std::vector<std::pair<std::string, Value>> updates; while (pos < tokens.size() && tokens[pos].type == SQLParser::IDENTIFIER) { std::string colName = tokens[pos++].value; if (pos < tokens.size() && tokens[pos].type == SQLParser::EQ) { pos++; // 跳过= Value value; if (tokens[pos].type == SQLParser::STRING) { value = tokens[pos].value; } else if (tokens[pos].type == SQLParser::NUMBER) { if (tokens[pos].value.find('.') != std::string::npos) { value = std::stod(tokens[pos].value); } else { value = std::stoi(tokens[pos].value); } } else if (tokens[pos].type == SQLParser::BOOL) { value = (tokens[pos].value == "TRUE"); } pos++; // 跳过值 updates.push_back({colName, value}); } if (pos < tokens.size() && tokens[pos].type == SQLParser::COMMA) { pos++; // 跳过逗号 } } // 解析WHERE条件 std::function<bool(const Row&)> condition = [](const Row&) { return true; }; if (pos < tokens.size() && tokens[pos].type == SQLParser::WHERE) { pos++; condition = parseCondition(tokens, pos, reader.getColumnMap()); } // 执行更新 auto& data = const_cast<Table&>(reader.getData()); // 移除const const auto& columnMap = reader.getColumnMap(); Table updatedRows; for (auto& row : data) { if (condition(row)) { // 该行满足更新条件 for (const auto& [colName, value] : updates) { auto it = columnMap.find(colName); if (it != columnMap.end()) { row[it->second] = value; } } updatedRows.push_back(row); } } return updatedRows; } // DELETE 语句执行 Table executeDelete(const std::vector<SQLParser::Token>& tokens) { // 实现删除逻辑 std::cout << "DELETE operation executed" << std::endl; return {}; } public: // 解析WHERE条件 std::function<bool(const Row&)> parseCondition( const std::vector<SQLParser::Token>& tokens, size_t& pos, const ColumnMap& columnMap) { // 递归解析条件表达式 return parseExpression(tokens, pos, columnMap); } std::function<bool(const Row&)> parseExpression( const std::vector<SQLParser::Token>& tokens, size_t& pos, const ColumnMap& columnMap) { auto left = parseTerm(tokens, pos, columnMap); while (pos < tokens.size()) { if (tokens[pos].type == SQLParser::AND) { pos++; auto right = parseTerm(tokens, pos, columnMap); return [=](const Row& row) { return left(row) && right(row); }; } else if (tokens[pos].type == SQLParser::OR) { pos++; auto right = parseTerm(tokens, pos, columnMap); return [=](const Row& row) { return left(row) || right(row); }; } else { break; } } return left; } std::function<bool(const Row&)> parseTerm( const std::vector<SQLParser::Token>& tokens, size_t& pos, const ColumnMap& columnMap) { if (tokens[pos].type == SQLParser::LPAREN) { pos++; auto expr = parseExpression(tokens, pos, columnMap); if (tokens[pos].type == SQLParser::RPAREN) pos++; return expr; } if (tokens[pos].type == SQLParser::NOT) { pos++; auto expr = parseTerm(tokens, pos, columnMap); return [=](const Row& row) { return !expr(row); }; } return parseComparison(tokens, pos, columnMap); } std::function<bool(const Row&)> parseComparison( const std::vector<SQLParser::Token>& tokens, size_t& pos, const ColumnMap& columnMap) { if (pos >= tokens.size()) return [](const Row&) { return false; }; std::string colName = tokens[pos].value; pos++; if (pos >= tokens.size()) return [](const Row&) { return false; }; SQLParser::TokenType op = tokens[pos].type; pos++; if (pos >= tokens.size()) return [](const Row&) { return false; }; Value value; if (tokens[pos].type == SQLParser::STRING) { value = tokens[pos].value; } else if (tokens[pos].type == SQLParser::NUMBER) { if (tokens[pos].value.find('.') != std::string::npos) { value = std::stod(tokens[pos].value); } else { value = std::stoi(tokens[pos].value); } } else if (tokens[pos].type == SQLParser::BOOL) { value = (tokens[pos].value == "TRUE"); } pos++; // 查找字段索引 int colIndex = -1; auto it = columnMap.find(colName); if (it != columnMap.end()) { colIndex = it->second; } else { // 如果直接查找失败,尝试其他匹配方式 for (const auto& pair : columnMap) { std::string mapKey = pair.first; std::string reqKey = colName; // 去除空格后比较 mapKey.erase(std::remove(mapKey.begin(), mapKey.end(), ' '), mapKey.end()); reqKey.erase(std::remove(reqKey.begin(), reqKey.end(), ' '), reqKey.end()); if (mapKey == reqKey) { colIndex = pair.second; break; } } } if (colIndex == -1) { return [](const Row&) { return false; }; } switch (op) { case SQLParser::EQ: return [=](const Row& row) { return row[colIndex] == value; }; case SQLParser::NEQ: return [=](const Row& row) { return row[colIndex] != value; }; case SQLParser::LT: return [=](const Row& row) { return compareValues(row[colIndex], value) < 0; }; case SQLParser::GT: return [=](const Row& row) { return compareValues(row[colIndex], value) > 0; }; case SQLParser::LTE: return [=](const Row& row) { return compareValues(row[colIndex], value) <= 0; }; case SQLParser::GTE: return [=](const Row& row) { return compareValues(row[colIndex], value) >= 0; }; default: return [](const Row&) { return false; }; } } int compareValues(const Value& a, const Value& b) { if (a.index() != b.index()) { // 尝试转换为相同类型 if (std::holds_alternative<int>(a) && std::holds_alternative<double>(b)) { return compareValues(Value(static_cast<double>(std::get<0>(a))), b); } if (std::holds_alternative<double>(a) && std::holds_alternative<int>(b)) { return compareValues(a, Value(static_cast<double>(std::get<0>(b)))); } return 0; // 不同类型无法比较 } switch (a.index()) { case 0: // int return (std::get<0>(a) == std::get<0>(b)) ? 0 : (std::get<0>(a) < std::get<0>(b)) ? -1 : 1; case 1: // double return (std::get<1>(a) == std::get<1>(b)) ? 0 : (std::get<1>(a) < std::get<1>(b)) ? -1 : 1; case 2: // string return std::get<2>(a).compare(std::get<2>(b)); case 3: // bool return static_cast<int>(std::get<3>(a)) - static_cast<int>(std::get<3>(b)); default: return 0; } } DBFReader& reader; }; // 结果打印函数 void printTable(const Table& table, const std::vector<std::string>& headers) { // 计算列宽 std::vector<size_t> widths(headers.size(), 0); for (size_t i = 0; i < headers.size(); i++) { widths[i] = headers[i].size(); } for (const auto& row : table) { for (size_t i = 0; i < row.size(); i++) { size_t len = 0; std::visit([&](const auto& arg) { using T = std::decay_t<decltype(arg)>; if constexpr (std::is_same_v<T, std::string>) { len = arg.size(); } else if constexpr (std::is_same_v<T, int> || std::is_same_v<T, double>) { std::ostringstream oss; oss << arg; len = oss.str().size(); } else if constexpr (std::is_same_v<T, bool>) { len = 5; // "false"的长度 } }, row[i]); widths[i] = std::max(widths[i], len); } } // 打印表头 for (size_t i = 0; i < headers.size(); i++) { std::cout << std::left << std::setw(widths[i] + 2) << headers[i]; } std::cout << "\n"; for (size_t i = 0; i < headers.size(); i++) { std::cout << std::string(widths[i] + 2, '-'); } std::cout << "\n"; // 打印数据 for (const auto& row : table) { for (size_t i = 0; i < row.size(); i++) { std::visit([&](const auto& arg) { using T = std::decay_t<decltype(arg)>; if constexpr (std::is_same_v<T, std::string>) { std::cout << std::left << std::setw(widths[i] + 2) << arg; } else if constexpr (std::is_same_v<T, int>) { std::cout << std::right << std::setw(widths[i] + 2) << arg; } else if constexpr (std::is_same_v<T, double>) { std::cout << std::fixed << std::setprecision(2) << std::right << std::setw(widths[i] + 2) << arg; } else if constexpr (std::is_same_v<T, bool>) { std::cout << std::right << std::setw(widths[i] + 2) << (arg ? "true" : "false"); } }, row[i]); } std::cout << "\n"; } } int main() { #ifdef _WIN32 // 设置控制台代码页为UTF-8,支持中文输出 SetConsoleOutputCP(CP_UTF8); SetConsoleCP(CP_UTF8); // 设置本地化以支持宽字符输出 std::setlocale(LC_ALL, "zh_CN.UTF-8"); #endif // 读取DBF文件 DBFReader reader("d:\\employees.dbf"); if (!reader.read()) { std::cerr << "Failed to read DBF file. Please make sure 'employees.dbf' exists in the current directory." << std::endl; // 列出当前目录下的文件以帮助调试 std::cout << "Files in current directory:" << std::endl; system("dir *.dbf"); return 1; } std::cout << "DBF file read successfully." << std::endl; // 创建SQL执行器 SQLExecutor executor(reader); // 显示字段信息 std::cout << "Fields in DBF file:" << std::endl; const auto& fields = reader.getFields(); for (const auto& field : fields) { std::string fieldName = processDBFFieldName(field.name, sizeof(field.name)); std::cout << " " << fieldName << " (type: " << field.type << ", length: " << (int)field.length << ")" << std::endl; } // 显示记录数量 std::cout << "Total records: " << reader.getData().size() << std::endl; // 执行SQL查询 - 显示所有记录 std::cout << "\nSQL: SELECT * FROM employees\n"; Table result = executor.execute("SELECT * FROM employees"); // 准备表头 std::vector<std::string> headers; for (const auto& field : reader.getFields()) { std::string fieldName = processDBFFieldName(field.name, sizeof(field.name)); headers.push_back(fieldName); } // 打印结果 printTable(result, headers); // 执行另一个查询 - 查询成绩大于80的学生 std::cout << "\nSQL: SELECT 姓名, 成绩 FROM employees WHERE 成绩 > 80\n"; result = executor.execute("SELECT 姓名, 成绩 FROM employees WHERE 成绩 > 80"); printTable(result, {"姓名", "成绩"}); // 查询特定班级的学生 std::cout << "\nSQL: SELECT 学号, 姓名, 班组 FROM employees WHERE 班组 = '一年一班 '\n"; result = executor.execute("SELECT 学号, 姓名, 班组 FROM employees WHERE 班组 = '一年一班'"); printTable(result, {"学号", "姓名", "班组"}); return 0; }以上代码,数据显示输出有中文乱码,请修复代码
08-16
AI Overview Implementing selective checkout with checkboxes on a cart page typically involves allowing users to select specific items in their cart for purchase, rather than requiring them to purchase all items. This functionality is not a standard feature in most e-commerce platforms and usually requires custom development or the use of specialized plugins/apps. General Approach: Modify the Cart Template: Add a checkbox next to each item in the cart. Ensure the checkbox is linked to the specific product or line item. Handle User Selections: Use JavaScript to detect changes in checkbox states (checked/unchecked). When a checkbox is selected or deselected, update the cart's subtotal and potentially the items that will be included in the checkout process. Adjust the Checkout Process: When the user proceeds to checkout, ensure that only the items corresponding to the selected checkboxes are passed to the order. This may involve modifying the platform's core checkout logic or using hooks/filters provided by the platform. Platform-Specific Considerations: WooCommerce (WordPress): Requires custom code in functions.php or a custom plugin to add checkboxes, manage selections, and modify the checkout process. You might use AJAX to update the cart dynamically as selections are made. Shopify: Involves modifying theme files (e.g., cart-template.liquid, theme.js) to add checkboxes and JavaScript to handle the logic. This often requires familiarity with Liquid (Shopify's templating language) and JavaScript. Other Platforms: The specific implementation will vary based on the platform's architecture and extensibility options. It may involve similar approaches of template modification and custom code. Important Notes: Complexity: Implementing selective checkout can be complex and may require advanced coding skills. User Experience: Carefully consider the user experience implications of selective checkout to ensure it is intuitive and does not cause confusion. Testing: Thoroughly test the functionality to ensure that selected items are correctly processed and that no loopholes exist (e.g., refreshing the page causing issues with selected items). I want to achieve this selective checkbox function in my cart page. Im using woocommerce and woodmart theme. Please advice on what to do
07-30
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值