Skip to content

4. 应用练习 - IMPOTS

4.1. 问题

我们计划编写一个程序来计算纳税人的税款。我们假设一个简化的情况:纳税人只有工资需要申报:

  • 计算员工nbParts的份额数:若未婚,则为nbEnfants/2 +1; 已婚则为 nbEnfants/2+2,其中 nbEnfants 为其子女数。
  • 计算其应税收入 R=0.72*S,其中 S 为其年薪
  • 计算其家庭系数 Q=R/N

根据以下数据计算其应纳税额 I

12620.0
0
0
13190
0.05
631
15640
0.1
1290.5
24740
0.15
2072.5
31810
0.2
3309.5
39970
0.25
4900
48360
0.3
6898.5
55790
0.35
9316.5
92970
0.4
12106
127860
0.45
16754.5
151250
0.50
23147.5
172040
0.55
30710
195000
0.60
39312
0
0.65
49062

每行有 3 个字段。要计算税款 I,需查找满足 QF<=字段1 的第一行。例如,若 QF=30000,则会找到该行

    24740        0.15        2072.5

此时税额 I 等于 0.15*R - 2072.5*nbParts。 如果 QF 使得关系 QF<=field1 从未成立,则使用最后一行中的系数。此处为:

    0                0.65        49062

由此得出的税额 I=0.65*R – 49062*nbParts。

4.2. 带数组的版本(impots_01)

我们提供一个初始程序,其中:

  • 计算税额所需的数据存储在三个数组中(限额、coeffR、coeffN)
  • 纳税人数据(已婚、子女、工资)存储在第一个文本文件中
  • 税款计算结果(已婚、子女、工资、税额)存储在第二个文本文件中

<?php

// 常量定义
$DATA = "data.txt";
$RESULTATS = "resultats.txt";
$limites = array(12620, 13190, 15640, 24740, 31810, 39970, 48360, 55790, 92970, 127860, 151250, 172040, 195000);
$coeffR = array(0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65);
$coeffN = array(0, 631, 1290.5, 2072.5, 3309.5, 4900, 6898.5, 9316.5, 12106, 16754.5, 23147.5, 30710, 39312, 49062);

// 读取数据
$data = fopen($DATA, "r");
if (!$data) {
  print "Impossible d'ouvrir en lecture le fichier des données [$DATA]\n";
  exit;
}

// 打开结果文件
$résultats = fopen($RESULTATS, "w");
if (!$résultats) {
  print "Impossible de créer le fichier des résultats [$RESULTATS]\n";
  exit;
}

// 处理数据文件的当前行
while ($ligne = fgets($data, 100)) {
  // 移除可能存在的换行符
  $ligne = cutNewLineChar($ligne);
  // 提取构成 $ligne 的 3 个字段:已婚、子女、工资
  list($marié, $enfants, $salaire) = explode(",", $ligne);
  // 计算税额
  $impôt = calculImpots($marié, $enfants, $salaire, $limites, $coeffR, $coeffN);
  // 将结果写入结果文件
  fputs($résultats, "$marié:$enfants:$salaire:$impôt\n");
  // 下一位纳税人
}
// 关闭文件
fclose($data);
fclose($résultats);

// 结束
exit;

// --------------------------------------------------------------------------
function cutNewLinechar($ligne) {
  // 若存在,则删除$ligne中的行尾标记
  $L = strlen($ligne);  // 行长度
  while (substr($ligne, $L - 1, 1) == "\n" or substr($ligne, $L - 1, 1) == "\r") {
    $ligne = substr($ligne, 0, $L - 1);
    $L--;
  }
  // 结束
  return($ligne);
}

// --------------------------------------------------------------------------
function calculImpots($marié, $enfants, $salaire, $limites, $coeffR, $coeffN) {
  // $marié:是,否
  // $enfants:子女数量
  // $salaire:年薪
  // $limites, $coeffR, $coeffN:用于计算税款的数据表
  
  // 份额数
  $marié = strtolower($marié);
  if ($marié == "oui")
    $nbParts = $enfants / 2 + 2;
  else
    $nbParts=$enfants / 2 + 1;
  // 若子女至少3名,则增加1/2份额
  if ($enfants >= 3)
    $nbParts+=0.5;
  // 应税收入
  $revenuImposable = 0.72 * $salaire;
  // 家庭系数
  $quotient = $revenuImposable / $nbParts;
  // 置于限额表末尾以终止后续循环
  array_push($limites, $quotient);
  // 计算税额
  $i = 0;
  while ($quotient > $limites[$i])
    $i++;
  // 由于将 $quotient 放置在 $limites 表的末尾,因此前面的循环
  // 不能超出数组 $limites
  // 现在可以计算税款
  return floor($revenuImposable * $coeffR[$i] - $nbParts * $coeffN[$i]);
}
?>

数据文件 data.txt(已婚、子女、工资):

oui,2,200000
non,2,200000
oui,3,200000
non,3,200000
oui,5,50000
non,0,3000000

résultats.txt文件(已婚、子女、工资、税款)的处理结果:

oui:2:200000:22504
non:2:200000:33388
oui:3:200000:16400
non:3:200000:22504
oui:5:50000:0
non:0:3000000:1354938

注释

  • 第4行:包含纳税人数据(已婚、子女、工资)的文本文件名
  • 第5行:包含税款计算结果(婚姻状况、子女、工资、税额)的文本文件名
  • 第6-8行:用于计算税款的三个数据表
  • 第11-15行:以只读方式打开纳税人数据文件
  • 第18-22行:以写入模式打开结果文件
  • 第25行:读取纳税人文件中各行数据(已婚、子女、工资)的循环
  • 第27行:移除行结束标记
  • 第29行:提取该行的组成部分(已婚、子女、工资)
  • 第31行:计算税额
  • 第33行:将税额存储到结果文件中
  • 第37-38行:完全处理完纳税人文件后,关闭文件
  • 第44行:该函数用于移除行$ligne中的行尾标记。在Windows系统中,行尾标记为字符串"\r\n";在Unix系统中,行尾标记为字符串"\n"。处理结果为去除行尾标记后的输入字符串。
  • 第47-48行:substr($chaîne,$début,$taille) 是 $chaîne 的子字符串,从字符 $début 开始,且长度不超过 $taille 个字符。
  • 第 63 行:strtolower($chaîne) 将其转换为小写 $chaîne
  • 第 76 行:array_push($tableau,$élément) 将 $élément 添加到数组 $tableau 的末尾。 需要注意的是,数组 $limites 是作为值传递给函数 calculerImpot56))。因此,此项元素的添加是在实际参数的副本上进行的,而该参数本身保持不变。
  • 第 84 行:floor($x) 函数返回 $x 的次小整数值。

4.3. 带文本文件的版本(impots_02)

此新版本与前一版本的唯一区别在于,计算税款所需的数据($limites、$coeffR、$coeffN)现已存储在文本文件中,格式如下:

文件 impots.txt

12620:13190:15640:24740:31810:39970:48360:55790:92970:127860:151250:172040:195000:0
0:0.05:0.1:0.15:0.2:0.25:0.3:0.35:0.4:0.45:0.5:0.55:0.6:0.65
0:631:1290.5:2072.5:3309.5:4900:6898.5:9316.5:12106:16754.5:23147.5:30710:39312:49062

新版本仅需读取 [impots.txt] 文件中的数据,并将它们放入三个表中($limites、$coeffR、$coeffN)。 这又回到了前一种情况。我们仅列出变更部分:


<?php

// 常量定义
$DATA = "data.txt";
$RESULTATS = "resultats.txt";
$IMPOTS = "impots.txt";

// 计算税款所需的数据已放入文件 $IMPOTS
// 每张表格占一行,格式为
// val1:val2:val3,...
list($erreur, $limites, $coeffR, $coeffN) = getTables($IMPOTS);

// 是否出现错误?
if ($erreur) {
  print "$erreur\n";
  exit;
}//if
// 读取用户数据
...

// 结束
exit;

// --------------------------------------------------------------------------
function cutNewLinechar($ligne) {
  // 如果存在,则删除 $ligne 的行尾标记
...
}

// --------------------------------------------------------------------------
function calculImpots($marié, $enfants, $salaire, $limites, $coeffR, $coeffN) {
  // $marié:是,否
  // $enfants:子女数量
  // $salaire:年薪
  // $limites, $coeffR, $coeffN:用于计算税款的数据表
  ...
}

// --------------------------------------------------------------------------
function getTables($IMPOTS) {
  // $IMPOTS: 包含表格 $limites、$coeffR、$coeffN 数据的文件名
  // 文件 $IMPOTS 是否存在?
  if (!file_exists($IMPOTS))
    return array("Le fichier $IMPOTS n'existe pas","","","");
  // 批量读取文件
  $tables = file($IMPOTS);
  if (!$tables)
    return array("Erreur lors de l'exploitation du fichier $IMPOTS","","","");
  // 创建 3 个表——假设行在语法上正确
  $limites = explode(":", cutNewLineChar($tables[0]));
  $coeffR = explode(":", cutNewLineChar($tables[1]));
  $coeffN = explode(":", cutNewLineChar($tables[2]));
  // 结束
  return array("", $limites, $coeffR, $coeffN);
}
?>

注释

  • 第 11 行:函数 getTables($IMPOTS) 利用文本文件 $IMPOTS 从中提取三个数组 ($limites、$coeffR、$coeffN)。生成的结果为($erreur、 $limites、$coeffR、$coeffN),其中 $erreur 是一个错误消息,若未发生错误则该文件为空。
  • 第 40 行:函数 getTables($IMPOTS)
  • 第 43 行: 函数 files_exists(nom_fichier) 返回布尔值 true(若文件 nom_fichier 存在),否则返回布尔值 false
  • 第 46 行:函数 file(nom_fichier) 读取整个文本文件 nom_fichier。它返回一个数组,其中每个元素代表文本文件中的一行。由于此处文件 impots.txt 包含三行文本,因此该函数将返回一个包含三个元素的数组。
  • 第50-52行:利用获取的这三行文本创建三个数组($limites、$coeffR、$coeffN

所得结果与上一版本相同。