使用 DBMS_OUTPUT 遍历文本时出现奇怪的输出

Strange output when looping through text with DBMS_OUTPUT

我正在编写一些代码来获取输入字符串并将其输出为多行以满足特定的行长度,我还需要它始终以新单词开始新行,这样单词就不会在行之间重叠难以阅读。

这可能不是最好的方法,但这是我想到的;

DECLARE
P_output_record          VARCHAR2(1000) := 'Well, Prince, so Genoa and Lucca are now just family estates of the Buonapartes. But I warn you, if you dont tell me that this means war, if you still try to defend the infamies and horrors perpetrated by that Antichrist I really believe he is Antichrist';
v_message_length         INTEGER;
v_written_chars          INTEGER;
v_new_line               VARCHAR2(30);
v_chars_to_write         INTEGER;
BEGIN
dbms_output.enable();
v_message_length := LENGTH(P_output_record);
v_written_chars := 0;

   WHILE(v_written_chars < v_message_length)
   LOOP
      IF(v_written_chars + 28 > v_message_length) THEN
        v_chars_to_write := (v_message_length - v_written_chars);
        v_new_line := SUBSTR( P_output_record, v_written_chars, v_chars_to_write);
      ELSE
        v_new_line := SUBSTR( P_output_record,v_written_chars, 28);
        --get the index of the last space so words dont spill to new lines
        v_chars_to_write := INSTR(v_new_line,' ', -1);
      END IF;
      DBMS_OUTPUT.PUT_LINE( SUBSTR( P_output_record, v_written_chars, v_chars_to_write-1));
      v_written_chars := v_written_chars + v_chars_to_write; 
   END LOOP;
END;

这是因为每一行都以一个新词开头,并且词不会溢出行。但是输出有点奇怪,第二行现在很重要 space 在哪里,或者完整的文本总是以 space 开头,我尝试将这些单词合并在一起但它只是从space 之前(但嘿,这是预料之中的),甚至一起尝试了不同的文本,但仍然得到以 space 开头的第二行的相同结果,这表明我的逻辑有问题,但我会假设如果这是我的逻辑那么它也会发生在其他线路上但事实并非如此。

在这里看到第二行前面有一个space。

Well, Prince, so Genoa and
 Lucca are now just family
estates of the Buonapartes.
But I warn you, if you dont
tell me that this means
war, if you still try to
defend the infamies and
horrors perpetrated by that
Antichrist I really believe
he is Antichri

P.S 我知道我在最后一行的末尾遗漏了一些字符,但这是另一个问题。

在此处添加 TRIM

DBMS_OUTPUT.PUT_LINE( trim(SUBSTR( P_output_record, v_written_chars, v_chars_to_write-1)));

Trim 适用于您的情况:

set serveroutput on;
DECLARE
P_output_record          VARCHAR2(1000) := 'Well, Prince, so Genoa and Lucca are now just family estates of the Buonapartes. But I warn you, if you dont tell me that this means war, if you still try to defend the infamies and horrors perpetrated by that Antichrist I really believe he is Antichrist';
v_message_length         INTEGER;
v_written_chars          INTEGER;
v_new_line               VARCHAR2(30);
v_chars_to_write         INTEGER;
BEGIN
dbms_output.enable();
v_message_length := LENGTH(P_output_record);
v_written_chars := 0;

   WHILE(v_written_chars < v_message_length)
   LOOP
      IF(v_written_chars + 28 > v_message_length) THEN
        v_chars_to_write := (v_message_length - v_written_chars);
        v_new_line := SUBSTR( P_output_record, v_written_chars, v_chars_to_write);
      ELSE
        v_new_line := SUBSTR( P_output_record,v_written_chars, 28);
        --get the index of the last space so words dont spill to new lines
        v_chars_to_write := INSTR(v_new_line,' ', -1);
      END IF;
      --See this line****************Edited line*************
      DBMS_OUTPUT.PUT_LINE( trim(SUBSTR( P_output_record, v_written_chars, v_chars_to_write-1)));
      v_written_chars := v_written_chars + v_chars_to_write; 
   END LOOP;
END;

希望对您有所帮助。

问题在于您使用 v_written_chars 来计算已写入的字符数并生成剩余部分的子字符串。

在第一次迭代中 v_written_chars = 0v_chars_to_write = 27,所以当您执行

v_written_chars := v_written_chars + v_chars_to_write;

你得到 v_written_chars = 27.

在第二次迭代中,您从第 27 个字符开始创建子字符串,这是一个空白 space,从而得到具有前导空白的字符串。

编辑代码的一种简单方法是初始化

v_written_chars = 1;

经过这次编辑,结果是:

Well, Prince, so Genoa and
Lucca are now just family
estates of the Buonapartes.
But I warn you, if you dont
tell me that this means
war, if you still try to
defend the infamies and
horrors perpetrated by that
Antichrist I really believe
he is Antichri

关于字符串的最后部分,一旦你在最后一行,你不需要计算要写的字符数,但你可以简单地做:

v_new_line := SUBSTR( P_output_record, v_written_chars);

经过编辑,您的代码变为:

...
BEGIN
dbms_output.enable();
v_message_length := LENGTH(P_output_record);
v_written_chars := 1;

   WHILE(v_written_chars < v_message_length)
   LOOP
      IF(v_written_chars + 28 > v_message_length) THEN
        v_new_line := SUBSTR( P_output_record, v_written_chars);
      ELSE
        v_new_line := SUBSTR( P_output_record,v_written_chars, 28);
        --get the index of the last space so words dont spill to new lines
        v_chars_to_write := INSTR(v_new_line,' ', -1);
      END IF;
      DBMS_OUTPUT.PUT_LINE( SUBSTR( P_output_record, v_written_chars, v_chars_to_write-1));
      v_written_chars := v_written_chars + v_chars_to_write; 
   END LOOP;
END;

结果是:

Well, Prince, so Genoa and
Lucca are now just family
estates of the Buonapartes.
But I warn you, if you dont
tell me that this means
war, if you still try to
defend the infamies and
horrors perpetrated by that
Antichrist I really believe
he is Antichrist

把这些放在一起只是为了好玩,您还可以执行以下操作(每行最多使用 80 个字符,但可以随意更改)。它还将多个 space 压缩为单个 space:

select listagg(val, ' ') within group (order by levl)
from (
    with t as (
      select 'Well, Prince, so Genoa and Lucca are now just family estates of the Buonapartes. But I warn you, if you dont tell me that this means war, if you still try to defend the infamies and horrors perpetrated by that Antichrist I really believe he is Antichrist' as str
      from dual )
    select levl, val, trunc(running_count/80)+1 as line
    from (
        select level as levl, regexp_substr(str,'[^ ]+',1,level) as val, sum(length(regexp_substr(str,'[^ ]+',1,level))) over (order by level) as running_count
        from t
        connect by regexp_substr(str,'[^ ]+',1,level) is not null
    )
)
group by line;

输出:

Well, Prince, so Genoa and Lucca are now just family estates of the Buonapartes. But I warn you, if you dont tell me that this means war, if you still try to defend the infamies and horrors perpetrated by that Antichrist I really believe he is Antichrist